
// Copyright Contributors to the OpenVDB Project
// SPDX-License-Identifier: MPL-2.0

/*!
    \file   PNanoVDB.h

    \author Andrew Reidmeyer

    \brief  This file is a portable (e.g. pointer-less) C99/GLSL/HLSL port 
	        of NanoVDB.h, which is compatible with most graphics APIs.
*/

#ifndef NANOVDB_PNANOVDB_H_HAS_BEEN_INCLUDED
#define NANOVDB_PNANOVDB_H_HAS_BEEN_INCLUDED

// ------------------------------------------------ Configuration -----------------------------------------------------------

// platforms
//#define PNANOVDB_C
//#define PNANOVDB_HLSL
//#define PNANOVDB_GLSL

// addressing mode
// PNANOVDB_ADDRESS_32
// PNANOVDB_ADDRESS_64
#if defined(PNANOVDB_C)
#ifndef PNANOVDB_ADDRESS_32
#define PNANOVDB_ADDRESS_64
#endif
#elif defined(PNANOVDB_HLSL)
#ifndef PNANOVDB_ADDRESS_64
#define PNANOVDB_ADDRESS_32
#endif
#elif defined(PNANOVDB_GLSL)
#ifndef PNANOVDB_ADDRESS_64
#define PNANOVDB_ADDRESS_32
#endif
#endif

// bounds checking
//#define PNANOVDB_BUF_BOUNDS_CHECK

// enable HDDA by default on HLSL/GLSL, make explicit on C
#if defined(PNANOVDB_C)
//#define PNANOVDB_HDDA
#ifdef PNANOVDB_HDDA
#ifndef PNANOVDB_CMATH
#define PNANOVDB_CMATH
#endif
#endif
#elif defined(PNANOVDB_HLSL)
#define PNANOVDB_HDDA
#elif defined(PNANOVDB_GLSL)
#define PNANOVDB_HDDA
#endif

#ifdef PNANOVDB_CMATH
#include <math.h>
#endif

// ------------------------------------------------ Buffer -----------------------------------------------------------

#if defined(PNANOVDB_BUF_CUSTOM)
// NOP
#elif defined(PNANOVDB_C)
#define PNANOVDB_BUF_C
#elif defined(PNANOVDB_HLSL)
#define PNANOVDB_BUF_HLSL
#elif defined(PNANOVDB_GLSL)
#define PNANOVDB_BUF_GLSL
#endif

#if defined(PNANOVDB_BUF_C)
#include <stdint.h>
#if defined(_WIN32)
#define PNANOVDB_BUF_FORCE_INLINE static inline __forceinline
#else
#define PNANOVDB_BUF_FORCE_INLINE static inline __attribute__((always_inline))
#endif
typedef struct pnanovdb_buf_t
{
	uint32_t* data;
#ifdef PNANOVDB_BUF_BOUNDS_CHECK
	uint64_t size_in_words;
#endif
}pnanovdb_buf_t;
PNANOVDB_BUF_FORCE_INLINE pnanovdb_buf_t pnanovdb_make_buf(uint32_t* data, uint64_t size_in_words)
{
	pnanovdb_buf_t ret;
	ret.data = data;
#ifdef PNANOVDB_BUF_BOUNDS_CHECK
	ret.size_in_words = size_in_words;
#endif
	return ret;
}
#if defined(PNANOVDB_ADDRESS_32)
PNANOVDB_BUF_FORCE_INLINE uint32_t pnanovdb_buf_read_uint32(pnanovdb_buf_t buf, uint32_t byte_offset)
{
	uint32_t wordaddress = (byte_offset >> 2u);
#ifdef PNANOVDB_BUF_BOUNDS_CHECK
	return wordaddress < buf.size_in_words ? buf.data[wordaddress] : 0u;
#else
	return buf.data[wordaddress];
#endif
}
PNANOVDB_BUF_FORCE_INLINE uint64_t pnanovdb_buf_read_uint64(pnanovdb_buf_t buf, uint32_t byte_offset)
{
	uint64_t* data64 = (uint64_t*)buf.data;
	uint32_t wordaddress64 = (byte_offset >> 3u);
#ifdef PNANOVDB_BUF_BOUNDS_CHECK
	uint64_t size_in_words64 = buf.size_in_words >> 1u;
	return wordaddress64 < size_in_words64 ? data64[wordaddress64] : 0llu;
#else
	return data64[wordaddress64];
#endif
}
#elif defined(PNANOVDB_ADDRESS_64)
PNANOVDB_BUF_FORCE_INLINE uint32_t pnanovdb_buf_read_uint32(pnanovdb_buf_t buf, uint64_t byte_offset)
{
	uint64_t wordaddress = (byte_offset >> 2u);
#ifdef PNANOVDB_BUF_BOUNDS_CHECK
	return wordaddress < buf.size_in_words ? buf.data[wordaddress] : 0u;
#else
	return buf.data[wordaddress];
#endif
}
PNANOVDB_BUF_FORCE_INLINE uint64_t pnanovdb_buf_read_uint64(pnanovdb_buf_t buf, uint64_t byte_offset)
{
	uint64_t* data64 = (uint64_t*)buf.data;
	uint64_t wordaddress64 = (byte_offset >> 3u);
#ifdef PNANOVDB_BUF_BOUNDS_CHECK
	uint64_t size_in_words64 = buf.size_in_words >> 1u;
	return wordaddress64 < size_in_words64 ? data64[wordaddress64] : 0llu;
#else
	return data64[wordaddress64];
#endif
}
#endif
typedef uint32_t pnanovdb_grid_type_t;
#define PNANOVDB_GRID_TYPE_GET(grid_typeIn, nameIn) pnanovdb_grid_type_constants[grid_typeIn].nameIn
#elif defined(PNANOVDB_BUF_HLSL)
#if defined(PNANOVDB_ADDRESS_32)
#define pnanovdb_buf_t StructuredBuffer<uint>
uint pnanovdb_buf_read_uint32(pnanovdb_buf_t buf, uint byte_offset)
{
	return buf[(byte_offset >> 2u)];
}
uint2 pnanovdb_buf_read_uint64(pnanovdb_buf_t buf, uint byte_offset)
{
	uint2 ret;
	ret.x = pnanovdb_buf_read_uint32(buf, byte_offset + 0u);
	ret.y = pnanovdb_buf_read_uint32(buf, byte_offset + 4u);
	return ret;
}
#elif defined(PNANOVDB_ADDRESS_64)
#define pnanovdb_buf_t StructuredBuffer<uint>
uint pnanovdb_buf_read_uint32(pnanovdb_buf_t buf, uint64_t byte_offset)
{
	return buf[uint(byte_offset >> 2u)];
}
uint64_t pnanovdb_buf_read_uint64(pnanovdb_buf_t buf, uint64_t byte_offset)
{
	uint64_t ret;
	ret = pnanovdb_buf_read_uint32(buf, byte_offset + 0u);
	ret = ret + (uint64_t(pnanovdb_buf_read_uint32(buf, byte_offset + 4u)) << 32u);
	return ret;
}
#endif
#define pnanovdb_grid_type_t uint
#define PNANOVDB_GRID_TYPE_GET(grid_typeIn, nameIn) pnanovdb_grid_type_constants[grid_typeIn].nameIn
#elif defined(PNANOVDB_BUF_GLSL)
struct pnanovdb_buf_t
{
	uint unused;	// to satisfy min struct size?
};
uint pnanovdb_buf_read_uint32(pnanovdb_buf_t buf, uint byte_offset)
{
	return pnanovdb_buf_data[(byte_offset >> 2u)];
}
uvec2 pnanovdb_buf_read_uint64(pnanovdb_buf_t buf, uint byte_offset)
{
	uvec2 ret;
	ret.x = pnanovdb_buf_read_uint32(buf, byte_offset + 0u);
	ret.y = pnanovdb_buf_read_uint32(buf, byte_offset + 4u);
	return ret;
}
#define pnanovdb_grid_type_t uint
#define PNANOVDB_GRID_TYPE_GET(grid_typeIn, nameIn) pnanovdb_grid_type_constants[grid_typeIn].nameIn
#endif

// ------------------------------------------------ Basic Types -----------------------------------------------------------

// force inline
#if defined(PNANOVDB_C)
#if defined(_WIN32)
#define PNANOVDB_FORCE_INLINE static inline __forceinline
#else
#define PNANOVDB_FORCE_INLINE static inline __attribute__((always_inline))
#endif
#elif defined(PNANOVDB_HLSL)
#define PNANOVDB_FORCE_INLINE
#elif defined(PNANOVDB_GLSL)
#define PNANOVDB_FORCE_INLINE
#endif

// struct typedef, static const, inout
#if defined(PNANOVDB_C)
#define PNANOVDB_STRUCT_TYPEDEF(X) typedef struct X X;
#define PNANOVDB_STATIC_CONST static const
#define PNANOVDB_INOUT(X) X*
#define PNANOVDB_IN(X) const X*
#define PNANOVDB_DEREF(X) (*X)
#define PNANOVDB_REF(X) &X
#elif defined(PNANOVDB_HLSL)
#define PNANOVDB_STRUCT_TYPEDEF(X)
#define PNANOVDB_STATIC_CONST static const
#define PNANOVDB_INOUT(X) inout X
#define PNANOVDB_IN(X) X
#define PNANOVDB_DEREF(X) X
#define PNANOVDB_REF(X) X
#elif defined(PNANOVDB_GLSL)
#define PNANOVDB_STRUCT_TYPEDEF(X)
#define PNANOVDB_STATIC_CONST const
#define PNANOVDB_INOUT(X) inout X
#define PNANOVDB_IN(X) X
#define PNANOVDB_DEREF(X) X
#define PNANOVDB_REF(X) X
#endif

// basic types, type conversion
#if defined(PNANOVDB_C)
#define PNANOVDB_NATIVE_64
#include <stdint.h>
#if !defined(PNANOVDB_MEMCPY_CUSTOM)
#include <string.h>
#define pnanovdb_memcpy memcpy
#endif
typedef uint32_t pnanovdb_uint32_t;
typedef int32_t pnanovdb_int32_t;
typedef int32_t pnanovdb_bool_t;
#define PNANOVDB_FALSE 0
#define PNANOVDB_TRUE 1
typedef uint64_t pnanovdb_uint64_t;
typedef int64_t pnanovdb_int64_t;
typedef struct pnanovdb_coord_t
{
	pnanovdb_int32_t x, y, z;
}pnanovdb_coord_t;
typedef struct pnanovdb_vec3_t
{
	float x, y, z;
}pnanovdb_vec3_t;
PNANOVDB_FORCE_INLINE pnanovdb_int32_t pnanovdb_uint32_as_int32(pnanovdb_uint32_t v) { return (pnanovdb_int32_t)v; }
PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_uint64_as_int64(pnanovdb_uint64_t v) { return (pnanovdb_int64_t)v; }
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_int64_as_uint64(pnanovdb_int64_t v) { return (pnanovdb_uint64_t)v; }
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_int32_as_uint32(pnanovdb_int32_t v) { return (pnanovdb_uint32_t)v; }
PNANOVDB_FORCE_INLINE float pnanovdb_uint32_as_float(pnanovdb_uint32_t v) { float vf; pnanovdb_memcpy(&vf, &v, sizeof(vf)); return vf; }
PNANOVDB_FORCE_INLINE double pnanovdb_uint64_as_double(pnanovdb_uint64_t v) { double vf; pnanovdb_memcpy(&vf, &v, sizeof(vf)); return vf; }
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_uint64_low(pnanovdb_uint64_t v) { return (pnanovdb_uint32_t)v; }
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_uint64_high(pnanovdb_uint64_t v) { return (pnanovdb_uint32_t)(v >> 32u); }
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_uint32_as_uint64(pnanovdb_uint32_t x, pnanovdb_uint32_t y) { return ((pnanovdb_uint64_t)x) | (((pnanovdb_uint64_t)y) << 32u); }
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_uint32_as_uint64_low(pnanovdb_uint32_t x) { return ((pnanovdb_uint64_t)x); }
PNANOVDB_FORCE_INLINE pnanovdb_int32_t pnanovdb_uint64_is_equal(pnanovdb_uint64_t a, pnanovdb_uint64_t b) { return a == b; }
PNANOVDB_FORCE_INLINE pnanovdb_int32_t pnanovdb_int64_is_zero(pnanovdb_int64_t a) { return a == 0; }
#ifdef PNANOVDB_CMATH
PNANOVDB_FORCE_INLINE float pnanovdb_floor(float v) { return floorf(v); }
#endif
PNANOVDB_FORCE_INLINE pnanovdb_int32_t pnanovdb_float_to_int32(float v) { return (pnanovdb_int32_t)v; }
PNANOVDB_FORCE_INLINE float pnanovdb_int32_to_float(pnanovdb_int32_t v) { return (float)v; }
PNANOVDB_FORCE_INLINE float pnanovdb_uint32_to_float(pnanovdb_uint32_t v) { return (float)v; }
PNANOVDB_FORCE_INLINE float pnanovdb_min(float a, float b) { return a < b ? a : b; }
PNANOVDB_FORCE_INLINE float pnanovdb_max(float a, float b) { return a > b ? a : b; }
#elif defined(PNANOVDB_HLSL)
typedef uint pnanovdb_uint32_t;
typedef int pnanovdb_int32_t;
typedef bool pnanovdb_bool_t;
#define PNANOVDB_FALSE false
#define PNANOVDB_TRUE true
typedef int3 pnanovdb_coord_t;
typedef float3 pnanovdb_vec3_t;
pnanovdb_int32_t pnanovdb_uint32_as_int32(pnanovdb_uint32_t v) { return int(v); }
pnanovdb_uint32_t pnanovdb_int32_as_uint32(pnanovdb_int32_t v) { return uint(v); }
float pnanovdb_uint32_as_float(pnanovdb_uint32_t v) { return asfloat(v); }
float pnanovdb_floor(float v) { return floor(v); }
pnanovdb_int32_t pnanovdb_float_to_int32(float v) { return int(v); }
float pnanovdb_int32_to_float(pnanovdb_int32_t v) { return float(v); }
float pnanovdb_uint32_to_float(pnanovdb_uint32_t v) { return float(v); }
float pnanovdb_min(float a, float b) { return min(a, b); }
float pnanovdb_max(float a, float b) { return max(a, b); }
#if defined(PNANOVDB_ADDRESS_32)
typedef uint2 pnanovdb_uint64_t;
typedef int2 pnanovdb_int64_t;
pnanovdb_int64_t pnanovdb_uint64_as_int64(pnanovdb_uint64_t v) { return int2(v); }
pnanovdb_uint64_t pnanovdb_int64_as_uint64(pnanovdb_int64_t v) { return uint2(v); }
double pnanovdb_uint64_as_double(pnanovdb_uint64_t v) { return asdouble(v.x, v.y); }
pnanovdb_uint32_t pnanovdb_uint64_low(pnanovdb_uint64_t v) { return v.x; }
pnanovdb_uint32_t pnanovdb_uint64_high(pnanovdb_uint64_t v) { return v.y; }
pnanovdb_uint64_t pnanovdb_uint32_as_uint64(pnanovdb_uint32_t x, pnanovdb_uint32_t y) { return uint2(x, y); }
pnanovdb_uint64_t pnanovdb_uint32_as_uint64_low(pnanovdb_uint32_t x) { return uint2(x, 0); }
bool pnanovdb_uint64_is_equal(pnanovdb_uint64_t a, pnanovdb_uint64_t b) { return (a.x == b.x) && (a.y == b.y); }
bool pnanovdb_int64_is_zero(pnanovdb_int64_t a) { return a.x == 0 && a.y == 0; }
#else
typedef uint64_t pnanovdb_uint64_t;
typedef int64_t pnanovdb_int64_t;
pnanovdb_int64_t pnanovdb_uint64_as_int64(pnanovdb_uint64_t v) { return int64_t(v); }
pnanovdb_uint64_t pnanovdb_int64_as_uint64(pnanovdb_int64_t v) { return uint64_t(v); }
double pnanovdb_uint64_as_double(pnanovdb_uint64_t v) { return asdouble(uint(v), uint(v >> 32u)); }
pnanovdb_uint32_t pnanovdb_uint64_low(pnanovdb_uint64_t v) { return uint(v); }
pnanovdb_uint32_t pnanovdb_uint64_high(pnanovdb_uint64_t v) { return uint(v >> 32u); }
pnanovdb_uint64_t pnanovdb_uint32_as_uint64(pnanovdb_uint32_t x, pnanovdb_uint32_t y) { return uint64_t(x) + (uint64_t(y) << 32u); }
pnanovdb_uint64_t pnanovdb_uint32_as_uint64_low(pnanovdb_uint32_t x) { return uint64_t(x); }
bool pnanovdb_uint64_is_equal(pnanovdb_uint64_t a, pnanovdb_uint64_t b) { return a == b; }
bool pnanovdb_int64_is_zero(pnanovdb_int64_t a) { return a == 0; }
#endif
#elif defined(PNANOVDB_GLSL)
#define pnanovdb_uint32_t uint
#define pnanovdb_int32_t int
#define pnanovdb_bool_t bool
#define PNANOVDB_FALSE false
#define PNANOVDB_TRUE true
#define pnanovdb_uint64_t uvec2
#define pnanovdb_int64_t ivec2
#define pnanovdb_coord_t ivec3
#define pnanovdb_vec3_t vec3
pnanovdb_int32_t pnanovdb_uint32_as_int32(pnanovdb_uint32_t v) { return int(v); }
pnanovdb_int64_t pnanovdb_uint64_as_int64(pnanovdb_uint64_t v) { return ivec2(v); }
pnanovdb_uint64_t pnanovdb_int64_as_uint64(pnanovdb_int64_t v) { return uvec2(v); }
pnanovdb_uint32_t pnanovdb_int32_as_uint32(pnanovdb_int32_t v) { return uint(v); }
float pnanovdb_uint32_as_float(pnanovdb_uint32_t v) { return uintBitsToFloat(v); }
double pnanovdb_uint64_as_double(pnanovdb_uint64_t v) { return packDouble2x32(uvec2(v.x, v.y)); }
pnanovdb_uint32_t pnanovdb_uint64_low(pnanovdb_uint64_t v) { return v.x; }
pnanovdb_uint32_t pnanovdb_uint64_high(pnanovdb_uint64_t v) { return v.y; }
pnanovdb_uint64_t pnanovdb_uint32_as_uint64(pnanovdb_uint32_t x, pnanovdb_uint32_t y) { return uvec2(x, y); }
pnanovdb_uint64_t pnanovdb_uint32_as_uint64_low(pnanovdb_uint32_t x) { return uvec2(x, 0); }
bool pnanovdb_uint64_is_equal(pnanovdb_uint64_t a, pnanovdb_uint64_t b) { return (a.x == b.x) && (a.y == b.y); }
bool pnanovdb_int64_is_zero(pnanovdb_int64_t a) { return a.x == 0 && a.y == 0; }
float pnanovdb_floor(float v) { return floor(v); }
pnanovdb_int32_t pnanovdb_float_to_int32(float v) { return int(v); }
float pnanovdb_int32_to_float(pnanovdb_int32_t v) { return float(v); }
float pnanovdb_uint32_to_float(pnanovdb_uint32_t v) { return float(v); }
float pnanovdb_min(float a, float b) { return min(a, b); }
float pnanovdb_max(float a, float b) { return max(a, b); }
#endif

// ------------------------------------------------ Coord/Vec3 Utilties -----------------------------------------------------------

#if defined(PNANOVDB_C)
PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_vec3_uniform(float a)
{
	pnanovdb_vec3_t v;
	v.x = a;
	v.y = a;
	v.z = a;
	return v;
}
PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_vec3_add(const pnanovdb_vec3_t a, const pnanovdb_vec3_t b)
{
	pnanovdb_vec3_t v;
	v.x = a.x + b.x;
	v.y = a.y + b.y;
	v.z = a.z + b.z;
	return v;
}
PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_vec3_sub(const pnanovdb_vec3_t a, const pnanovdb_vec3_t b)
{
	pnanovdb_vec3_t v;
	v.x = a.x - b.x;
	v.y = a.y - b.y;
	v.z = a.z - b.z;
	return v;
}
PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_vec3_mul(const pnanovdb_vec3_t a, const pnanovdb_vec3_t b)
{
	pnanovdb_vec3_t v;
	v.x = a.x * b.x;
	v.y = a.y * b.y;
	v.z = a.z * b.z;
	return v;
}
PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_vec3_div(const pnanovdb_vec3_t a, const pnanovdb_vec3_t b)
{
	pnanovdb_vec3_t v;
	v.x = a.x / b.x;
	v.y = a.y / b.y;
	v.z = a.z / b.z;
	return v;
}
PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_vec3_min(const pnanovdb_vec3_t a, const pnanovdb_vec3_t b)
{
	pnanovdb_vec3_t v;
	v.x = a.x < b.x ? a.x : b.x;
	v.y = a.y < b.y ? a.y : b.y;
	v.z = a.z < b.z ? a.z : b.z;
	return v;
}
PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_vec3_max(const pnanovdb_vec3_t a, const pnanovdb_vec3_t b)
{
	pnanovdb_vec3_t v;
	v.x = a.x > b.x ? a.x : b.x;
	v.y = a.y > b.y ? a.y : b.y;
	v.z = a.z > b.z ? a.z : b.z;
	return v;
}
PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_coord_to_vec3(const pnanovdb_coord_t coord)
{
	pnanovdb_vec3_t v;
	v.x = pnanovdb_int32_to_float(coord.x);
	v.y = pnanovdb_int32_to_float(coord.y);
	v.z = pnanovdb_int32_to_float(coord.z);
	return v;
}
PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_coord_uniform(const pnanovdb_int32_t a)
{
	pnanovdb_coord_t v;
	v.x = a;
	v.y = a;
	v.z = a;
	return v;
}
PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_coord_add(pnanovdb_coord_t a, pnanovdb_coord_t b)
{
	pnanovdb_coord_t v;
	v.x = a.x + b.x;
	v.y = a.y + b.y;
	v.z = a.z + b.z;
	return v;
}
#elif defined(PNANOVDB_HLSL)
pnanovdb_vec3_t pnanovdb_vec3_uniform(float a) { return float3(a, a, a); }
pnanovdb_vec3_t pnanovdb_vec3_add(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return a + b; }
pnanovdb_vec3_t pnanovdb_vec3_sub(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return a - b; }
pnanovdb_vec3_t pnanovdb_vec3_mul(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return a * b; }
pnanovdb_vec3_t pnanovdb_vec3_div(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return a / b; }
pnanovdb_vec3_t pnanovdb_vec3_min(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return min(a, b); }
pnanovdb_vec3_t pnanovdb_vec3_max(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return max(a, b); }
pnanovdb_vec3_t pnanovdb_coord_to_vec3(pnanovdb_coord_t coord) { return float3(coord); }
pnanovdb_coord_t pnanovdb_coord_uniform(pnanovdb_int32_t a) { return int3(a, a, a); }
pnanovdb_coord_t pnanovdb_coord_add(pnanovdb_coord_t a, pnanovdb_coord_t b) { return a + b; }
#elif defined(PNANOVDB_GLSL)
pnanovdb_vec3_t pnanovdb_vec3_uniform(float a) { return vec3(a, a, a); }
pnanovdb_vec3_t pnanovdb_vec3_add(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return a + b; }
pnanovdb_vec3_t pnanovdb_vec3_sub(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return a - b; }
pnanovdb_vec3_t pnanovdb_vec3_mul(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return a * b; }
pnanovdb_vec3_t pnanovdb_vec3_div(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return a / b; }
pnanovdb_vec3_t pnanovdb_vec3_min(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return min(a, b); }
pnanovdb_vec3_t pnanovdb_vec3_max(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return max(a, b); }
pnanovdb_vec3_t pnanovdb_coord_to_vec3(const pnanovdb_coord_t coord) { return vec3(coord); }
pnanovdb_coord_t pnanovdb_coord_uniform(pnanovdb_int32_t a) { return ivec3(a, a, a); }
pnanovdb_coord_t pnanovdb_coord_add(pnanovdb_coord_t a, pnanovdb_coord_t b) { return a + b; }
#endif

// ------------------------------------------------ Address Type -----------------------------------------------------------

#if defined(PNANOVDB_ADDRESS_32)
struct pnanovdb_address_t
{
	pnanovdb_uint32_t byte_offset;
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_address_t)

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_offset(pnanovdb_address_t address, pnanovdb_uint32_t byte_offset)
{
	pnanovdb_address_t ret = address;
	ret.byte_offset += byte_offset;
	return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_offset_neg(pnanovdb_address_t address, pnanovdb_uint32_t byte_offset)
{
	pnanovdb_address_t ret = address;
	ret.byte_offset -= byte_offset;
	return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_offset_product(pnanovdb_address_t address, pnanovdb_uint32_t byte_offset, pnanovdb_uint32_t multiplier)
{
	pnanovdb_address_t ret = address;
	ret.byte_offset += byte_offset * multiplier;
	return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_offset64(pnanovdb_address_t address, pnanovdb_uint64_t byte_offset)
{
	pnanovdb_address_t ret = address;
	// lose high bits on 32-bit
	ret.byte_offset += pnanovdb_uint64_low(byte_offset);
	return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_address_mask(pnanovdb_address_t address, pnanovdb_uint32_t mask)
{
	return address.byte_offset & mask;
}
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_mask_inv(pnanovdb_address_t address, pnanovdb_uint32_t mask)
{
	pnanovdb_address_t ret = address;
	ret.byte_offset &= (~mask);
	return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_null()
{
	pnanovdb_address_t ret = { 0 };
	return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_address_is_null(pnanovdb_address_t address)
{
	return address.byte_offset == 0u;
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_address_in_interval(pnanovdb_address_t address, pnanovdb_address_t min_address, pnanovdb_address_t max_address)
{
	return address.byte_offset >= min_address.byte_offset && address.byte_offset < max_address.byte_offset;
}
#elif defined(PNANOVDB_ADDRESS_64)
struct pnanovdb_address_t
{
	pnanovdb_uint64_t byte_offset;
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_address_t)

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_offset(pnanovdb_address_t address, pnanovdb_uint32_t byte_offset)
{
	pnanovdb_address_t ret = address;
	ret.byte_offset += byte_offset;
	return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_offset_neg(pnanovdb_address_t address, pnanovdb_uint32_t byte_offset)
{
	pnanovdb_address_t ret = address;
	ret.byte_offset -= byte_offset;
	return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_offset_product(pnanovdb_address_t address, pnanovdb_uint32_t byte_offset, pnanovdb_uint32_t multiplier)
{
	pnanovdb_address_t ret = address;
	ret.byte_offset += pnanovdb_uint32_as_uint64_low(byte_offset) * pnanovdb_uint32_as_uint64_low(multiplier);
	return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_offset64(pnanovdb_address_t address, pnanovdb_uint64_t byte_offset)
{
	pnanovdb_address_t ret = address;
	ret.byte_offset += byte_offset;
	return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_address_mask(pnanovdb_address_t address, pnanovdb_uint32_t mask)
{
	return pnanovdb_uint64_low(address.byte_offset) & mask;
}
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_mask_inv(pnanovdb_address_t address, pnanovdb_uint32_t mask)
{
	pnanovdb_address_t ret = address;
	ret.byte_offset &= (~pnanovdb_uint32_as_uint64_low(mask));
	return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_null()
{
	pnanovdb_address_t ret = { 0 };
	return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_address_is_null(pnanovdb_address_t address)
{
	return address.byte_offset == 0llu;
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_address_in_interval(pnanovdb_address_t address, pnanovdb_address_t min_address, pnanovdb_address_t max_address)
{
	return address.byte_offset >= min_address.byte_offset && address.byte_offset < max_address.byte_offset;
}
#endif

// ------------------------------------------------ High Level Buffer Read -----------------------------------------------------------

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_read_uint32(pnanovdb_buf_t buf, pnanovdb_address_t address)
{
	return pnanovdb_buf_read_uint32(buf, address.byte_offset);
}
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_read_uint64(pnanovdb_buf_t buf, pnanovdb_address_t address)
{
	return pnanovdb_buf_read_uint64(buf, address.byte_offset);
}
PNANOVDB_FORCE_INLINE pnanovdb_int32_t pnanovdb_read_int32(pnanovdb_buf_t buf, pnanovdb_address_t address)
{
	return pnanovdb_uint32_as_int32(pnanovdb_read_uint32(buf, address));
}
PNANOVDB_FORCE_INLINE float pnanovdb_read_float(pnanovdb_buf_t buf, pnanovdb_address_t address)
{
	return pnanovdb_uint32_as_float(pnanovdb_read_uint32(buf, address));
}
PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_read_int64(pnanovdb_buf_t buf, pnanovdb_address_t address)
{
	return pnanovdb_uint64_as_int64(pnanovdb_read_uint64(buf, address));
}
PNANOVDB_FORCE_INLINE double pnanovdb_read_double(pnanovdb_buf_t buf, pnanovdb_address_t address)
{
	return pnanovdb_uint64_as_double(pnanovdb_read_uint64(buf, address));
}
PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_read_coord(pnanovdb_buf_t buf, pnanovdb_address_t address)
{
	pnanovdb_coord_t ret;
	ret.x = pnanovdb_uint32_as_int32(pnanovdb_read_uint32(buf, pnanovdb_address_offset(address, 0u)));
	ret.y = pnanovdb_uint32_as_int32(pnanovdb_read_uint32(buf, pnanovdb_address_offset(address, 4u)));
	ret.z = pnanovdb_uint32_as_int32(pnanovdb_read_uint32(buf, pnanovdb_address_offset(address, 8u)));
	return ret;
}

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_read_bit(pnanovdb_buf_t buf, pnanovdb_address_t address, pnanovdb_uint32_t bit_offset)
{
	pnanovdb_address_t word_address = pnanovdb_address_mask_inv(address, 3u);
	pnanovdb_uint32_t bit_index = (pnanovdb_address_mask(address, 3u) << 3u) + bit_offset;
	pnanovdb_uint32_t value_word = pnanovdb_buf_read_uint32(buf, word_address.byte_offset);
	return ((value_word >> bit_index) & 1) != 0u;
}

#if defined(PNANOVDB_C)
PNANOVDB_FORCE_INLINE short pnanovdb_read_half(pnanovdb_buf_t buf, pnanovdb_address_t address)
{
	pnanovdb_uint32_t raw = pnanovdb_read_uint32(buf, address);
	return (short)(raw >> (pnanovdb_address_mask(address, 2) << 3));
}
#elif defined(PNANOVDB_HLSL)
PNANOVDB_FORCE_INLINE float pnanovdb_read_half(pnanovdb_buf_t buf, pnanovdb_address_t address)
{
	pnanovdb_uint32_t raw = pnanovdb_read_uint32(buf, address);
	return f16tof32(raw >> (pnanovdb_address_mask(address, 2) << 3));
}
#elif defined(PNANOVDB_GLSL)
PNANOVDB_FORCE_INLINE float pnanovdb_read_half(pnanovdb_buf_t buf, pnanovdb_address_t address)
{
	pnanovdb_uint32_t raw = pnanovdb_read_uint32(buf, address);
	return unpackHalf2x16(raw >> (pnanovdb_address_mask(address, 2) << 3)).x;
}
#endif

// ------------------------------------------------ Core Structures -----------------------------------------------------------

#define PNANOVDB_MAGIC_NUMBER 0x304244566f6e614eUL// "NanoVDB0" in hex - little endian (uint64_t)

#define PNANOVDB_MAJOR_VERSION_NUMBER 32// reflects changes to the ABI
#define PNANOVDB_MINOR_VERSION_NUMBER  3// reflects changes to the API but not ABI
#define PNANOVDB_PATCH_VERSION_NUMBER  0// reflects bug-fixes with no ABI or API changes

#define PNANOVDB_GRID_TYPE_UNKNOWN 0
#define PNANOVDB_GRID_TYPE_FLOAT 1
#define PNANOVDB_GRID_TYPE_DOUBLE 2
#define PNANOVDB_GRID_TYPE_INT16 3
#define PNANOVDB_GRID_TYPE_INT32 4
#define PNANOVDB_GRID_TYPE_INT64 5
#define PNANOVDB_GRID_TYPE_VEC3F 6
#define PNANOVDB_GRID_TYPE_VEC3D 7
#define PNANOVDB_GRID_TYPE_MASK 8
#define PNANOVDB_GRID_TYPE_HALF 9
#define PNANOVDB_GRID_TYPE_UINT32 10
#define PNANOVDB_GRID_TYPE_BOOLEAN 11
#define PNANOVDB_GRID_TYPE_RGBA8 12
#define PNANOVDB_GRID_TYPE_FP4 13
#define PNANOVDB_GRID_TYPE_FP8 14
#define PNANOVDB_GRID_TYPE_FP16 15
#define PNANOVDB_GRID_TYPE_FPN 16
#define PNANOVDB_GRID_TYPE_VEC4F 17
#define PNANOVDB_GRID_TYPE_VEC4D 18
#define PNANOVDB_GRID_TYPE_END 19

#define PNANOVDB_GRID_CLASS_UNKNOWN 0
#define PNANOVDB_GRID_CLASS_LEVEL_SET 1		// narrow band levelset, e.g. SDF
#define PNANOVDB_GRID_CLASS_FOG_VOLUME 2	// fog volume, e.g. density
#define PNANOVDB_GRID_CLASS_STAGGERED 3		// staggered MAC grid, e.g. velocity
#define PNANOVDB_GRID_CLASS_POINT_INDEX 4	// point index grid
#define PNANOVDB_GRID_CLASS_POINT_DATA 5	// point data grid
#define PNANOVDB_GRID_CLASS_TOPOLOGY 6		// grid with active states only (no values)
#define PNANOVDB_GRID_CLASS_VOXEL_VOLUME 7	// volume of geometric cubes, e.g. minecraft
#define PNANOVDB_GRID_CLASS_END 8

#define PNANOVDB_GRID_FLAGS_HAS_LONG_GRID_NAME (1 << 0)
#define PNANOVDB_GRID_FLAGS_HAS_BBOX (1 << 1)
#define PNANOVDB_GRID_FLAGS_HAS_MIN_MAX (1 << 2)
#define PNANOVDB_GRID_FLAGS_HAS_AVERAGE (1 << 3)
#define PNANOVDB_GRID_FLAGS_HAS_STD_DEVIATION (1 << 4)
#define PNANOVDB_GRID_FLAGS_IS_BREADTH_FIRST (1 << 5)
#define PNANOVDB_GRID_FLAGS_END (1 << 6)

#define PNANOVDB_LEAF_TYPE_DEFAULT 0
#define PNANOVDB_LEAF_TYPE_LITE 1
#define PNANOVDB_LEAF_TYPE_FP 2

PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_value_strides_bits[PNANOVDB_GRID_TYPE_END]  = {  0, 32, 64, 16, 32, 64, 96,  192,  0, 16, 32,  1, 32,  4,  8, 16,  0, 128, 256 };
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_table_strides_bits[PNANOVDB_GRID_TYPE_END] =  { 64, 64, 64, 64, 64, 64, 128, 192, 64, 64, 64, 64, 64, 64, 64, 64, 64, 128, 256 };
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_minmax_strides_bits[PNANOVDB_GRID_TYPE_END] = {  0, 32, 64, 16, 32, 64, 96,  192,  8, 16, 32,  8, 32, 32, 32, 32, 32, 128, 256 };
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_minmax_aligns_bits[PNANOVDB_GRID_TYPE_END]  = {  0, 32, 64, 16, 32, 64, 32,   64,  8, 16, 32,  8, 32, 32, 32, 32, 32,  32,  64 };
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_stat_strides_bits[PNANOVDB_GRID_TYPE_END]   = {  0, 32, 64, 32, 32, 64, 32,   64,  8, 32, 32,  8, 32, 32, 32, 32, 32,  32,  64 };
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_leaf_type[PNANOVDB_GRID_TYPE_END]           = {  0,  0,  0,  0,  0,  0,  0,    0,  1,  0,  0,  1,  0,  2,  2,  2,  2,   0,   0 };

struct pnanovdb_map_t
{
	float matf[9];
	float invmatf[9];
	float vecf[3];
	float taperf;
	double matd[9];
	double invmatd[9];
	double vecd[3];
	double taperd;
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_map_t)
struct pnanovdb_map_handle_t { pnanovdb_address_t address; };
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_map_handle_t)

#define PNANOVDB_MAP_SIZE 264

#define PNANOVDB_MAP_OFF_MATF 0
#define PNANOVDB_MAP_OFF_INVMATF 36
#define PNANOVDB_MAP_OFF_VECF 72
#define PNANOVDB_MAP_OFF_TAPERF 84
#define PNANOVDB_MAP_OFF_MATD 88
#define PNANOVDB_MAP_OFF_INVMATD 160
#define PNANOVDB_MAP_OFF_VECD 232
#define PNANOVDB_MAP_OFF_TAPERD 256

PNANOVDB_FORCE_INLINE float pnanovdb_map_get_matf(pnanovdb_buf_t buf, pnanovdb_map_handle_t p, pnanovdb_uint32_t index) {
	return pnanovdb_read_float(buf, pnanovdb_address_offset(p.address, PNANOVDB_MAP_OFF_MATF + 4u * index));
}
PNANOVDB_FORCE_INLINE float pnanovdb_map_get_invmatf(pnanovdb_buf_t buf, pnanovdb_map_handle_t p, pnanovdb_uint32_t index) {
	return pnanovdb_read_float(buf, pnanovdb_address_offset(p.address, PNANOVDB_MAP_OFF_INVMATF + 4u * index));
}
PNANOVDB_FORCE_INLINE float pnanovdb_map_get_vecf(pnanovdb_buf_t buf, pnanovdb_map_handle_t p, pnanovdb_uint32_t index) {
	return pnanovdb_read_float(buf, pnanovdb_address_offset(p.address, PNANOVDB_MAP_OFF_VECF + 4u * index));
}
PNANOVDB_FORCE_INLINE float pnanovdb_map_get_taperf(pnanovdb_buf_t buf, pnanovdb_map_handle_t p, pnanovdb_uint32_t index) {
	return pnanovdb_read_float(buf, pnanovdb_address_offset(p.address, PNANOVDB_MAP_OFF_TAPERF));
}
PNANOVDB_FORCE_INLINE double pnanovdb_map_get_matd(pnanovdb_buf_t buf, pnanovdb_map_handle_t p, pnanovdb_uint32_t index) {
	return pnanovdb_read_double(buf, pnanovdb_address_offset(p.address, PNANOVDB_MAP_OFF_MATD + 8u * index));
}
PNANOVDB_FORCE_INLINE double pnanovdb_map_get_invmatd(pnanovdb_buf_t buf, pnanovdb_map_handle_t p, pnanovdb_uint32_t index) {
	return pnanovdb_read_double(buf, pnanovdb_address_offset(p.address, PNANOVDB_MAP_OFF_INVMATD + 8u * index));
}
PNANOVDB_FORCE_INLINE double pnanovdb_map_get_vecd(pnanovdb_buf_t buf, pnanovdb_map_handle_t p, pnanovdb_uint32_t index) {
	return pnanovdb_read_double(buf, pnanovdb_address_offset(p.address, PNANOVDB_MAP_OFF_VECD + 8u * index));
}
PNANOVDB_FORCE_INLINE double pnanovdb_map_get_taperd(pnanovdb_buf_t buf, pnanovdb_map_handle_t p, pnanovdb_uint32_t index) {
	return pnanovdb_read_double(buf, pnanovdb_address_offset(p.address, PNANOVDB_MAP_OFF_TAPERD));
}

struct pnanovdb_grid_t
{
	pnanovdb_uint64_t magic;					// 8 bytes, 	0
	pnanovdb_uint64_t checksum;					// 8 bytes,		8
	pnanovdb_uint32_t version;					// 4 bytes,		16
	pnanovdb_uint32_t flags;					// 4 bytes,		20
	pnanovdb_uint32_t grid_index;				// 4 bytes,		24
	pnanovdb_uint32_t grid_count;				// 4 bytes,		28
	pnanovdb_uint64_t grid_size;				// 8 bytes,		32
	pnanovdb_uint32_t grid_name[256 / 4];		// 256 bytes, 	40
	pnanovdb_map_t map;							// 264 bytes,	296
	double world_bbox[6];						// 48 bytes,	560
	double voxel_size[3];						// 24 bytes,	608
	pnanovdb_uint32_t grid_class;				// 4 bytes,		632
	pnanovdb_uint32_t grid_type;				// 4 bytes,		636
	pnanovdb_int64_t blind_metadata_offset;		// 8 bytes,		640
	pnanovdb_uint32_t blind_metadata_count;		// 4 bytes,		648
	pnanovdb_uint32_t pad[5];					// 20 bytes,	652
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_grid_t)
struct pnanovdb_grid_handle_t { pnanovdb_address_t address; };
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_grid_handle_t)

#define PNANOVDB_GRID_SIZE 672

#define PNANOVDB_GRID_OFF_MAGIC 0
#define PNANOVDB_GRID_OFF_CHECKSUM 8
#define PNANOVDB_GRID_OFF_VERSION 16
#define PNANOVDB_GRID_OFF_FLAGS 20
#define PNANOVDB_GRID_OFF_GRID_INDEX 24
#define PNANOVDB_GRID_OFF_GRID_COUNT 28
#define PNANOVDB_GRID_OFF_GRID_SIZE 32
#define PNANOVDB_GRID_OFF_GRID_NAME 40
#define PNANOVDB_GRID_OFF_MAP 296
#define PNANOVDB_GRID_OFF_WORLD_BBOX 560
#define PNANOVDB_GRID_OFF_VOXEL_SIZE 608
#define PNANOVDB_GRID_OFF_GRID_CLASS 632
#define PNANOVDB_GRID_OFF_GRID_TYPE 636
#define PNANOVDB_GRID_OFF_BLIND_METADATA_OFFSET 640
#define PNANOVDB_GRID_OFF_BLIND_METADATA_COUNT 648

PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_grid_get_magic(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
	return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_MAGIC));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_grid_get_checksum(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
	return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_CHECKSUM));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_grid_get_version(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_VERSION));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_grid_get_flags(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_FLAGS));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_grid_get_grid_index(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_GRID_INDEX));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_grid_get_grid_count(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_GRID_COUNT));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_grid_get_grid_size(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
	return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_GRID_SIZE));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_grid_get_grid_name(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p, pnanovdb_uint32_t index) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_GRID_NAME + 4u * index));
}
PNANOVDB_FORCE_INLINE pnanovdb_map_handle_t pnanovdb_grid_get_map(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
	pnanovdb_map_handle_t ret;
	ret.address = pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_MAP);
	return ret;
}
PNANOVDB_FORCE_INLINE double pnanovdb_grid_get_world_bbox(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p, pnanovdb_uint32_t index) {
	return pnanovdb_read_double(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_WORLD_BBOX + 8u * index));
}
PNANOVDB_FORCE_INLINE double pnanovdb_grid_get_voxel_size(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p, pnanovdb_uint32_t index) {
	return pnanovdb_read_double(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_VOXEL_SIZE + 8u * index));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_grid_get_grid_class(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_GRID_CLASS));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_grid_get_grid_type(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_GRID_TYPE));
}
PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_grid_get_blind_metadata_offset(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
	return pnanovdb_read_int64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_BLIND_METADATA_OFFSET));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_grid_get_blind_metadata_count(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) { 
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_BLIND_METADATA_COUNT));
}

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_version_get_major(pnanovdb_uint32_t version)
{
	return (version >> 21u) & ((1u << 11u) - 1u);
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_version_get_minor(pnanovdb_uint32_t version)
{
	return (version >> 10u) & ((1u << 11u) - 1u);
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_version_get_patch(pnanovdb_uint32_t version)
{
	return version & ((1u << 10u) - 1u);
}

struct pnanovdb_gridblindmetadata_t
{
	pnanovdb_int64_t byte_offset;		// 8 bytes,		0
	pnanovdb_uint64_t element_count;	// 8 bytes,		8
	pnanovdb_uint32_t flags;			// 4 bytes,		16
	pnanovdb_uint32_t semantic;			// 4 bytes,		20
	pnanovdb_uint32_t data_class;		// 4 bytes,		24
	pnanovdb_uint32_t data_type;		// 4 bytes,		28
	pnanovdb_uint32_t name[256 / 4];	// 256 bytes,	32
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_gridblindmetadata_t)
struct pnanovdb_gridblindmetadata_handle_t { pnanovdb_address_t address; };
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_gridblindmetadata_handle_t)

#define PNANOVDB_GRIDBLINDMETADATA_SIZE 288

#define PNANOVDB_GRIDBLINDMETADATA_OFF_BYTE_OFFSET 0
#define PNANOVDB_GRIDBLINDMETADATA_OFF_ELEMENT_COUNT 8
#define PNANOVDB_GRIDBLINDMETADATA_OFF_FLAGS 16
#define PNANOVDB_GRIDBLINDMETADATA_OFF_SEMANTIC 20
#define PNANOVDB_GRIDBLINDMETADATA_OFF_DATA_CLASS 24
#define PNANOVDB_GRIDBLINDMETADATA_OFF_DATA_TYPE 28
#define PNANOVDB_GRIDBLINDMETADATA_OFF_NAME 32

PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_gridblindmetadata_get_byte_offset(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p) {
	return pnanovdb_read_int64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_BYTE_OFFSET));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_gridblindmetadata_get_element_count(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p) {
	return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_ELEMENT_COUNT));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_gridblindmetadata_get_flags(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_FLAGS));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_gridblindmetadata_get_semantic(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_SEMANTIC));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_gridblindmetadata_get_data_class(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_DATA_CLASS));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_gridblindmetadata_get_data_type(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_DATA_TYPE));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_gridblindmetadata_get_name(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p, pnanovdb_uint32_t index) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_NAME + 4u * index));
}

struct pnanovdb_tree_t
{
	pnanovdb_uint64_t node_offset_leaf;
	pnanovdb_uint64_t node_offset_lower;
	pnanovdb_uint64_t node_offset_upper;
	pnanovdb_uint64_t node_offset_root;	
	pnanovdb_uint32_t node_count_leaf;
	pnanovdb_uint32_t node_count_lower;
	pnanovdb_uint32_t node_count_upper;
	pnanovdb_uint32_t tile_count_leaf;
	pnanovdb_uint32_t tile_count_lower;
	pnanovdb_uint32_t tile_count_upper;
	pnanovdb_uint64_t voxel_count;
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_tree_t)
struct pnanovdb_tree_handle_t { pnanovdb_address_t address; };
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_tree_handle_t)

#define PNANOVDB_TREE_SIZE 64

#define PNANOVDB_TREE_OFF_NODE_OFFSET_LEAF 0
#define PNANOVDB_TREE_OFF_NODE_OFFSET_LOWER 8
#define PNANOVDB_TREE_OFF_NODE_OFFSET_UPPER 16
#define PNANOVDB_TREE_OFF_NODE_OFFSET_ROOT 24
#define PNANOVDB_TREE_OFF_NODE_COUNT_LEAF 32
#define PNANOVDB_TREE_OFF_NODE_COUNT_LOWER 36
#define PNANOVDB_TREE_OFF_NODE_COUNT_UPPER 40
#define PNANOVDB_TREE_OFF_TILE_COUNT_LEAF 44
#define PNANOVDB_TREE_OFF_TILE_COUNT_LOWER 48
#define PNANOVDB_TREE_OFF_TILE_COUNT_UPPER 52
#define PNANOVDB_TREE_OFF_VOXEL_COUNT 56

PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_tree_get_node_offset_leaf(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
	return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_NODE_OFFSET_LEAF));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_tree_get_node_offset_lower(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
	return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_NODE_OFFSET_LOWER));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_tree_get_node_offset_upper(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
	return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_NODE_OFFSET_UPPER));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_tree_get_node_offset_root(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
	return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_NODE_OFFSET_ROOT));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_tree_get_node_count_leaf(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_NODE_COUNT_LEAF));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_tree_get_node_count_lower(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_NODE_COUNT_LOWER));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_tree_get_node_count_upper(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_NODE_COUNT_UPPER));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_tree_get_tile_count_leaf(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_TILE_COUNT_LEAF));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_tree_get_tile_count_lower(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_TILE_COUNT_LOWER));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_tree_get_tile_count_upper(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_TILE_COUNT_UPPER));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_tree_get_voxel_count(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
	return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_VOXEL_COUNT));
}

struct pnanovdb_root_t
{
	pnanovdb_coord_t bbox_min;
	pnanovdb_coord_t bbox_max;
	pnanovdb_uint32_t table_size;
	pnanovdb_uint32_t pad1;			// background can start here
	// background, min, max
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_root_t)
struct pnanovdb_root_handle_t { pnanovdb_address_t address; };
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_root_handle_t)

#define PNANOVDB_ROOT_BASE_SIZE 28

#define PNANOVDB_ROOT_OFF_BBOX_MIN 0
#define PNANOVDB_ROOT_OFF_BBOX_MAX 12
#define PNANOVDB_ROOT_OFF_TABLE_SIZE 24

PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_root_get_bbox_min(pnanovdb_buf_t buf, pnanovdb_root_handle_t p) {
	return pnanovdb_read_coord(buf, pnanovdb_address_offset(p.address, PNANOVDB_ROOT_OFF_BBOX_MIN));
}
PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_root_get_bbox_max(pnanovdb_buf_t buf, pnanovdb_root_handle_t p) {
	return pnanovdb_read_coord(buf, pnanovdb_address_offset(p.address, PNANOVDB_ROOT_OFF_BBOX_MAX));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_root_get_tile_count(pnanovdb_buf_t buf, pnanovdb_root_handle_t p) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_ROOT_OFF_TABLE_SIZE));
}

struct pnanovdb_root_tile_t
{
	pnanovdb_uint64_t key;
	pnanovdb_int64_t child;			// signed byte offset from root to the child node, 0 means it is a constant tile, so use value
	pnanovdb_uint32_t state;
	pnanovdb_uint32_t pad1;			// value can start here
	// value
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_root_tile_t)
struct pnanovdb_root_tile_handle_t { pnanovdb_address_t address; };
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_root_tile_handle_t)

#define PNANOVDB_ROOT_TILE_BASE_SIZE 20

#define PNANOVDB_ROOT_TILE_OFF_KEY 0
#define PNANOVDB_ROOT_TILE_OFF_CHILD 8
#define PNANOVDB_ROOT_TILE_OFF_STATE 16

PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_root_tile_get_key(pnanovdb_buf_t buf, pnanovdb_root_tile_handle_t p) {
	return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_ROOT_TILE_OFF_KEY));
}
PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_root_tile_get_child(pnanovdb_buf_t buf, pnanovdb_root_tile_handle_t p) { 
	return pnanovdb_read_int64(buf, pnanovdb_address_offset(p.address, PNANOVDB_ROOT_TILE_OFF_CHILD));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_root_tile_get_state(pnanovdb_buf_t buf, pnanovdb_root_tile_handle_t p) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_ROOT_TILE_OFF_STATE));
}

struct pnanovdb_upper_t
{
	pnanovdb_coord_t bbox_min;
	pnanovdb_coord_t bbox_max;
	pnanovdb_uint64_t flags;
	pnanovdb_uint32_t value_mask[1024];
	pnanovdb_uint32_t child_mask[1024];
	// min, max
	// alignas(32) pnanovdb_uint32_t table[];
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_upper_t)
struct pnanovdb_upper_handle_t { pnanovdb_address_t address; };
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_upper_handle_t)

#define PNANOVDB_UPPER_TABLE_COUNT 32768
#define PNANOVDB_UPPER_BASE_SIZE 8224

#define PNANOVDB_UPPER_OFF_BBOX_MIN 0
#define PNANOVDB_UPPER_OFF_BBOX_MAX 12
#define PNANOVDB_UPPER_OFF_FLAGS 24
#define PNANOVDB_UPPER_OFF_VALUE_MASK 32
#define PNANOVDB_UPPER_OFF_CHILD_MASK 4128

PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_upper_get_bbox_min(pnanovdb_buf_t buf, pnanovdb_upper_handle_t p) {
	return pnanovdb_read_coord(buf, pnanovdb_address_offset(p.address, PNANOVDB_UPPER_OFF_BBOX_MIN));
}
PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_upper_get_bbox_max(pnanovdb_buf_t buf, pnanovdb_upper_handle_t p) {
	return pnanovdb_read_coord(buf, pnanovdb_address_offset(p.address, PNANOVDB_UPPER_OFF_BBOX_MAX));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_upper_get_flags(pnanovdb_buf_t buf, pnanovdb_upper_handle_t p) {
	return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_UPPER_OFF_FLAGS));
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_upper_get_value_mask(pnanovdb_buf_t buf, pnanovdb_upper_handle_t p, pnanovdb_uint32_t bit_index) {
	pnanovdb_uint32_t value = pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_UPPER_OFF_VALUE_MASK + 4u * (bit_index >> 5u)));
	return ((value >> (bit_index & 31u)) & 1) != 0u;
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_upper_get_child_mask(pnanovdb_buf_t buf, pnanovdb_upper_handle_t p, pnanovdb_uint32_t bit_index) {
	pnanovdb_uint32_t value = pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_UPPER_OFF_CHILD_MASK + 4u * (bit_index >> 5u)));
	return ((value >> (bit_index & 31u)) & 1) != 0u;
}

struct pnanovdb_lower_t
{
	pnanovdb_coord_t bbox_min;
	pnanovdb_coord_t bbox_max;
	pnanovdb_uint64_t flags;
	pnanovdb_uint32_t value_mask[128];
	pnanovdb_uint32_t child_mask[128];
	// min, max
	// alignas(32) pnanovdb_uint32_t table[];
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_lower_t)
struct pnanovdb_lower_handle_t { pnanovdb_address_t address; };
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_lower_handle_t)

#define PNANOVDB_LOWER_TABLE_COUNT 4096
#define PNANOVDB_LOWER_BASE_SIZE 1056

#define PNANOVDB_LOWER_OFF_BBOX_MIN 0
#define PNANOVDB_LOWER_OFF_BBOX_MAX 12
#define PNANOVDB_LOWER_OFF_FLAGS 24
#define PNANOVDB_LOWER_OFF_VALUE_MASK 32
#define PNANOVDB_LOWER_OFF_CHILD_MASK 544

PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_lower_get_bbox_min(pnanovdb_buf_t buf, pnanovdb_lower_handle_t p) {
	return pnanovdb_read_coord(buf, pnanovdb_address_offset(p.address, PNANOVDB_LOWER_OFF_BBOX_MIN));
}
PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_lower_get_bbox_max(pnanovdb_buf_t buf, pnanovdb_lower_handle_t p) {
	return pnanovdb_read_coord(buf, pnanovdb_address_offset(p.address, PNANOVDB_LOWER_OFF_BBOX_MAX));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_lower_get_flags(pnanovdb_buf_t buf, pnanovdb_lower_handle_t p) {
	return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_LOWER_OFF_FLAGS));
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_lower_get_value_mask(pnanovdb_buf_t buf, pnanovdb_lower_handle_t p, pnanovdb_uint32_t bit_index) {
	pnanovdb_uint32_t value = pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_LOWER_OFF_VALUE_MASK + 4u * (bit_index >> 5u)));
	return ((value >> (bit_index & 31u)) & 1) != 0u;
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_lower_get_child_mask(pnanovdb_buf_t buf, pnanovdb_lower_handle_t p, pnanovdb_uint32_t bit_index) {
	pnanovdb_uint32_t value = pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_LOWER_OFF_CHILD_MASK + 4u * (bit_index >> 5u)));
	return ((value >> (bit_index & 31u)) & 1) != 0u;
}

struct pnanovdb_leaf_t
{
	pnanovdb_coord_t bbox_min;
	pnanovdb_uint32_t bbox_dif_and_flags;
	pnanovdb_uint32_t value_mask[16];
	// min, max
	// alignas(32) pnanovdb_uint32_t values[];
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_leaf_t)
struct pnanovdb_leaf_handle_t { pnanovdb_address_t address; };
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_leaf_handle_t)

#define PNANOVDB_LEAF_TABLE_COUNT 512
#define PNANOVDB_LEAF_BASE_SIZE 80

#define PNANOVDB_LEAF_OFF_BBOX_MIN 0
#define PNANOVDB_LEAF_OFF_BBOX_DIF_AND_FLAGS 12
#define PNANOVDB_LEAF_OFF_VALUE_MASK 16

#define PNANOVDB_LEAF_TABLE_NEG_OFF_BBOX_DIF_AND_FLAGS 84
#define PNANOVDB_LEAF_TABLE_NEG_OFF_MINIMUM 16
#define PNANOVDB_LEAF_TABLE_NEG_OFF_QUANTUM 12

PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_leaf_get_bbox_min(pnanovdb_buf_t buf, pnanovdb_leaf_handle_t p) {
	return pnanovdb_read_coord(buf, pnanovdb_address_offset(p.address, PNANOVDB_LEAF_OFF_BBOX_MIN));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_leaf_get_bbox_dif_and_flags(pnanovdb_buf_t buf, pnanovdb_leaf_handle_t p) {
	return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_LEAF_OFF_BBOX_DIF_AND_FLAGS));
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_leaf_get_value_mask(pnanovdb_buf_t buf, pnanovdb_leaf_handle_t p, pnanovdb_uint32_t bit_index) {
	pnanovdb_uint32_t value = pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_LEAF_OFF_VALUE_MASK + 4u * (bit_index >> 5u)));
	return ((value >> (bit_index & 31u)) & 1) != 0u;
}

struct pnanovdb_grid_type_constants_t
{
	pnanovdb_uint32_t root_off_background;
	pnanovdb_uint32_t root_off_min;
	pnanovdb_uint32_t root_off_max;
	pnanovdb_uint32_t root_off_ave;
	pnanovdb_uint32_t root_off_stddev;
	pnanovdb_uint32_t root_size;
	pnanovdb_uint32_t value_stride_bits;
	pnanovdb_uint32_t table_stride;
	pnanovdb_uint32_t root_tile_off_value;
	pnanovdb_uint32_t root_tile_size;
	pnanovdb_uint32_t upper_off_min;
	pnanovdb_uint32_t upper_off_max;
	pnanovdb_uint32_t upper_off_ave;
	pnanovdb_uint32_t upper_off_stddev;
	pnanovdb_uint32_t upper_off_table;
	pnanovdb_uint32_t upper_size;
	pnanovdb_uint32_t lower_off_min;
	pnanovdb_uint32_t lower_off_max;
	pnanovdb_uint32_t lower_off_ave;
	pnanovdb_uint32_t lower_off_stddev;
	pnanovdb_uint32_t lower_off_table;
	pnanovdb_uint32_t lower_size;
	pnanovdb_uint32_t leaf_off_min;
	pnanovdb_uint32_t leaf_off_max;
	pnanovdb_uint32_t leaf_off_ave;
	pnanovdb_uint32_t leaf_off_stddev;
	pnanovdb_uint32_t leaf_off_table;
	pnanovdb_uint32_t leaf_size;
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_grid_type_constants_t)

PNANOVDB_STATIC_CONST pnanovdb_grid_type_constants_t pnanovdb_grid_type_constants[PNANOVDB_GRID_TYPE_END] =
{
	{28, 28, 28, 28, 28, 32,  0, 8, 20, 32,  8224, 8224, 8224, 8224, 8224, 270368,  1056, 1056, 1056, 1056, 1056, 33824,  80, 80, 80, 80, 96, 96},
	{28, 32, 36, 40, 44, 64,  32, 8, 20, 32,  8224, 8228, 8232, 8236, 8256, 270400,  1056, 1060, 1064, 1068, 1088, 33856,  80, 84, 88, 92, 96, 2144},
	{32, 40, 48, 56, 64, 96,  64, 8, 24, 32,  8224, 8232, 8240, 8248, 8256, 270400,  1056, 1064, 1072, 1080, 1088, 33856,  80, 88, 96, 104, 128, 4224},
	{28, 30, 32, 36, 40, 64,  16, 8, 20, 32,  8224, 8226, 8228, 8232, 8256, 270400,  1056, 1058, 1060, 1064, 1088, 33856,  80, 82, 84, 88, 96, 1120},
	{28, 32, 36, 40, 44, 64,  32, 8, 20, 32,  8224, 8228, 8232, 8236, 8256, 270400,  1056, 1060, 1064, 1068, 1088, 33856,  80, 84, 88, 92, 96, 2144},
	{32, 40, 48, 56, 64, 96,  64, 8, 24, 32,  8224, 8232, 8240, 8248, 8256, 270400,  1056, 1064, 1072, 1080, 1088, 33856,  80, 88, 96, 104, 128, 4224},
	{28, 40, 52, 64, 68, 96,  96, 16, 20, 32,  8224, 8236, 8248, 8252, 8256, 532544,  1056, 1068, 1080, 1084, 1088, 66624,  80, 92, 104, 108, 128, 6272},
	{32, 56, 80, 104, 112, 128,  192, 24, 24, 64,  8224, 8248, 8272, 8280, 8288, 794720,  1056, 1080, 1104, 1112, 1120, 99424,  80, 104, 128, 136, 160, 12448},
	{28, 29, 30, 31, 32, 64,  0, 8, 20, 32,  8224, 8225, 8226, 8227, 8256, 270400,  1056, 1057, 1058, 1059, 1088, 33856,  80, 80, 80, 80, 96, 96},
	{28, 30, 32, 36, 40, 64,  16, 8, 20, 32,  8224, 8226, 8228, 8232, 8256, 270400,  1056, 1058, 1060, 1064, 1088, 33856,  80, 82, 84, 88, 96, 1120},
	{28, 32, 36, 40, 44, 64,  32, 8, 20, 32,  8224, 8228, 8232, 8236, 8256, 270400,  1056, 1060, 1064, 1068, 1088, 33856,  80, 84, 88, 92, 96, 2144},
	{28, 29, 30, 31, 32, 64,  1, 8, 20, 32,  8224, 8225, 8226, 8227, 8256, 270400,  1056, 1057, 1058, 1059, 1088, 33856,  80, 80, 80, 80, 96, 160},
	{28, 32, 36, 40, 44, 64,  32, 8, 20, 32,  8224, 8228, 8232, 8236, 8256, 270400,  1056, 1060, 1064, 1068, 1088, 33856,  80, 84, 88, 92, 96, 2144},
	{28, 32, 36, 40, 44, 64,  0, 8, 20, 32,  8224, 8228, 8232, 8236, 8256, 270400,  1056, 1060, 1064, 1068, 1088, 33856,  88, 90, 92, 94, 96, 352},
	{28, 32, 36, 40, 44, 64,  0, 8, 20, 32,  8224, 8228, 8232, 8236, 8256, 270400,  1056, 1060, 1064, 1068, 1088, 33856,  88, 90, 92, 94, 96, 608},
	{28, 32, 36, 40, 44, 64,  0, 8, 20, 32,  8224, 8228, 8232, 8236, 8256, 270400,  1056, 1060, 1064, 1068, 1088, 33856,  88, 90, 92, 94, 96, 1120},
	{28, 32, 36, 40, 44, 64,  0, 8, 20, 32,  8224, 8228, 8232, 8236, 8256, 270400,  1056, 1060, 1064, 1068, 1088, 33856,  88, 90, 92, 94, 96, 96},
	{28, 44, 60, 76, 80, 96,  128, 16, 20, 64,  8224, 8240, 8256, 8260, 8288, 532576,  1056, 1072, 1088, 1092, 1120, 66656,  80, 96, 112, 116, 128, 8320},
	{32, 64, 96, 128, 136, 160,  256, 32, 24, 64,  8224, 8256, 8288, 8296, 8320, 1056896,  1056, 1088, 1120, 1128, 1152, 132224,  80, 112, 144, 152, 160, 16544},
};

// ------------------------------------------------ Basic Lookup -----------------------------------------------------------

PNANOVDB_FORCE_INLINE pnanovdb_gridblindmetadata_handle_t pnanovdb_grid_get_gridblindmetadata(pnanovdb_buf_t buf, pnanovdb_grid_handle_t grid, pnanovdb_uint32_t index)
{
	pnanovdb_gridblindmetadata_handle_t meta = { grid.address };
	pnanovdb_uint64_t byte_offset = pnanovdb_grid_get_blind_metadata_offset(buf, grid);
	meta.address = pnanovdb_address_offset64(meta.address, byte_offset);
	meta.address = pnanovdb_address_offset_product(meta.address, PNANOVDB_GRIDBLINDMETADATA_SIZE, index);
	return meta;
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanodvb_grid_get_gridblindmetadata_value_address(pnanovdb_buf_t buf, pnanovdb_grid_handle_t grid, pnanovdb_uint32_t index)
{
	pnanovdb_gridblindmetadata_handle_t meta = pnanovdb_grid_get_gridblindmetadata(buf, grid, index);
	pnanovdb_int64_t byte_offset = pnanovdb_gridblindmetadata_get_byte_offset(buf, meta);
	pnanovdb_address_t address = grid.address;
	address = pnanovdb_address_offset64(address, pnanovdb_int64_as_uint64(byte_offset));
	return address;
}

PNANOVDB_FORCE_INLINE pnanovdb_tree_handle_t pnanovdb_grid_get_tree(pnanovdb_buf_t buf, pnanovdb_grid_handle_t grid)
{
	pnanovdb_tree_handle_t tree = { grid.address };
	tree.address = pnanovdb_address_offset(tree.address, PNANOVDB_GRID_SIZE);
	return tree;
}

PNANOVDB_FORCE_INLINE pnanovdb_root_handle_t pnanovdb_tree_get_root(pnanovdb_buf_t buf, pnanovdb_tree_handle_t tree)
{
	pnanovdb_root_handle_t root = { tree.address };
	pnanovdb_uint64_t byte_offset = pnanovdb_tree_get_node_offset_root(buf, tree);
	root.address = pnanovdb_address_offset64(root.address, byte_offset);
	return root;
}

PNANOVDB_FORCE_INLINE pnanovdb_root_tile_handle_t pnanovdb_root_get_tile(pnanovdb_grid_type_t grid_type, pnanovdb_root_handle_t root, pnanovdb_uint32_t n)
{
	pnanovdb_root_tile_handle_t tile = { root.address };
	tile.address = pnanovdb_address_offset(tile.address, PNANOVDB_GRID_TYPE_GET(grid_type, root_size));
	tile.address = pnanovdb_address_offset_product(tile.address, PNANOVDB_GRID_TYPE_GET(grid_type, root_tile_size), n);
	return tile;
}

PNANOVDB_FORCE_INLINE pnanovdb_root_tile_handle_t pnanovdb_root_get_tile_zero(pnanovdb_grid_type_t grid_type, pnanovdb_root_handle_t root)
{
	pnanovdb_root_tile_handle_t tile = { root.address };
	tile.address = pnanovdb_address_offset(tile.address, PNANOVDB_GRID_TYPE_GET(grid_type, root_size));
	return tile;
}

PNANOVDB_FORCE_INLINE pnanovdb_upper_handle_t pnanovdb_root_get_child(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root, pnanovdb_root_tile_handle_t tile)
{
	pnanovdb_upper_handle_t upper = { root.address };
	upper.address = pnanovdb_address_offset64(upper.address, pnanovdb_int64_as_uint64(pnanovdb_root_tile_get_child(buf, tile)));
	return upper;
}

PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_coord_to_key(PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
#if defined(PNANOVDB_NATIVE_64)
	pnanovdb_uint64_t iu = pnanovdb_int32_as_uint32(PNANOVDB_DEREF(ijk).x) >> 12u;
	pnanovdb_uint64_t ju = pnanovdb_int32_as_uint32(PNANOVDB_DEREF(ijk).y) >> 12u;
	pnanovdb_uint64_t ku = pnanovdb_int32_as_uint32(PNANOVDB_DEREF(ijk).z) >> 12u;
	return (ku) | (ju << 21u) | (iu << 42u);
#else
	pnanovdb_uint32_t iu = pnanovdb_int32_as_uint32(PNANOVDB_DEREF(ijk).x) >> 12u;
	pnanovdb_uint32_t ju = pnanovdb_int32_as_uint32(PNANOVDB_DEREF(ijk).y) >> 12u;
	pnanovdb_uint32_t ku = pnanovdb_int32_as_uint32(PNANOVDB_DEREF(ijk).z) >> 12u;
	pnanovdb_uint32_t key_x = ku | (ju << 21);
	pnanovdb_uint32_t key_y = (iu << 10) | (ju >> 11);
	return pnanovdb_uint32_as_uint64(key_x, key_y);
#endif
}

PNANOVDB_FORCE_INLINE pnanovdb_root_tile_handle_t pnanovdb_root_find_tile(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
	pnanovdb_uint32_t tile_count = pnanovdb_uint32_as_int32(pnanovdb_root_get_tile_count(buf, root));
	pnanovdb_root_tile_handle_t tile = pnanovdb_root_get_tile_zero(grid_type, root);
	pnanovdb_uint64_t key = pnanovdb_coord_to_key(ijk);
	for (pnanovdb_uint32_t i = 0u; i < tile_count; i++)
	{
		if (pnanovdb_uint64_is_equal(key, pnanovdb_root_tile_get_key(buf, tile)))
		{
			return tile;
		}
		tile.address = pnanovdb_address_offset(tile.address, PNANOVDB_GRID_TYPE_GET(grid_type, root_tile_size));
	}
	pnanovdb_root_tile_handle_t null_handle = { pnanovdb_address_null() };
	return null_handle;
}

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_leaf_coord_to_offset(PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
	return (((PNANOVDB_DEREF(ijk).x & 7) >> 0) << (2 * 3)) +
		(((PNANOVDB_DEREF(ijk).y & 7) >> 0) << (3)) +
		((PNANOVDB_DEREF(ijk).z & 7) >> 0);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_leaf_get_min_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_leaf_handle_t node)
{
	pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, leaf_off_min);
	return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_leaf_get_max_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_leaf_handle_t node)
{
	pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, leaf_off_max);
	return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_leaf_get_ave_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_leaf_handle_t node)
{
	pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, leaf_off_ave);
	return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_leaf_get_stddev_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_leaf_handle_t node)
{
	pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, leaf_off_stddev);
	return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_leaf_get_table_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_leaf_handle_t node, pnanovdb_uint32_t n)
{
	pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, leaf_off_table) + ((PNANOVDB_GRID_TYPE_GET(grid_type, value_stride_bits) * n) >> 3u);
	return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_leaf_get_value_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_leaf_handle_t leaf, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
	pnanovdb_uint32_t n = pnanovdb_leaf_coord_to_offset(ijk);
	return pnanovdb_leaf_get_table_address(grid_type, buf, leaf, n);
}

PNANOVDB_FORCE_INLINE float pnanovdb_leaf_fp_read_float(pnanovdb_buf_t buf, pnanovdb_address_t address, PNANOVDB_IN(pnanovdb_coord_t) ijk, pnanovdb_uint32_t value_log_bits)
{
	// 	value_log_bits															//   2     3       4
	pnanovdb_uint32_t value_bits = 1u << value_log_bits;						//   4     8      16
	pnanovdb_uint32_t value_mask = (1u << value_bits) - 1u;						// 0xF  0xFF  0xFFFF
	pnanovdb_uint32_t values_per_word_bits = 5u - value_log_bits;				//   3     2       1
	pnanovdb_uint32_t values_per_word_mask = (1u << values_per_word_bits) - 1u; //   7     3       1

	pnanovdb_uint32_t n = pnanovdb_leaf_coord_to_offset(ijk);
	float minimum = pnanovdb_read_float(buf, pnanovdb_address_offset_neg(address, PNANOVDB_LEAF_TABLE_NEG_OFF_MINIMUM));
	float quantum = pnanovdb_read_float(buf, pnanovdb_address_offset_neg(address, PNANOVDB_LEAF_TABLE_NEG_OFF_QUANTUM));
	pnanovdb_uint32_t raw = pnanovdb_read_uint32(buf, pnanovdb_address_offset(address, ((n >> values_per_word_bits) << 2u)));
	pnanovdb_uint32_t value_compressed = (raw >> ((n & values_per_word_mask) << value_log_bits)) & value_mask;
	return pnanovdb_uint32_to_float(value_compressed) * quantum + minimum;
}

PNANOVDB_FORCE_INLINE float pnanovdb_leaf_fp4_read_float(pnanovdb_buf_t buf, pnanovdb_address_t address, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
	return pnanovdb_leaf_fp_read_float(buf, address, ijk, 2u);
}

PNANOVDB_FORCE_INLINE float pnanovdb_leaf_fp8_read_float(pnanovdb_buf_t buf, pnanovdb_address_t address, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
	return pnanovdb_leaf_fp_read_float(buf, address, ijk, 3u);
}

PNANOVDB_FORCE_INLINE float pnanovdb_leaf_fp16_read_float(pnanovdb_buf_t buf, pnanovdb_address_t address, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
	return pnanovdb_leaf_fp_read_float(buf, address, ijk, 4u);
}

PNANOVDB_FORCE_INLINE float pnanovdb_leaf_fpn_read_float(pnanovdb_buf_t buf, pnanovdb_address_t address, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
	pnanovdb_uint32_t bbox_dif_and_flags = pnanovdb_read_uint32(buf, pnanovdb_address_offset_neg(address, PNANOVDB_LEAF_TABLE_NEG_OFF_BBOX_DIF_AND_FLAGS));
	pnanovdb_uint32_t flags = bbox_dif_and_flags >> 24u;
	pnanovdb_uint32_t value_log_bits = flags >> 5; // b = 0, 1, 2, 3, 4 corresponding to 1, 2, 4, 8, 16 bits
	return pnanovdb_leaf_fp_read_float(buf, address, ijk, value_log_bits);
}

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_lower_coord_to_offset(PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
	return (((PNANOVDB_DEREF(ijk).x & 127) >> 3) << (2 * 4)) +
		(((PNANOVDB_DEREF(ijk).y & 127) >> 3) << (4)) +
		((PNANOVDB_DEREF(ijk).z & 127) >> 3);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_lower_get_min_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t node)
{
	pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, lower_off_min);
	return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_lower_get_max_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t node)
{
	pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, lower_off_max);
	return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_lower_get_ave_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t node)
{
	pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, lower_off_ave);
	return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_lower_get_stddev_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t node)
{
	pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, lower_off_stddev);
	return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_lower_get_table_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t node, pnanovdb_uint32_t n)
{
	pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, lower_off_table) + PNANOVDB_GRID_TYPE_GET(grid_type, table_stride) * n;
	return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_lower_get_table_child(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t node, pnanovdb_uint32_t n)
{
	pnanovdb_address_t table_address = pnanovdb_lower_get_table_address(grid_type, buf, node, n);
	return pnanovdb_read_int64(buf, table_address);
}

PNANOVDB_FORCE_INLINE pnanovdb_leaf_handle_t pnanovdb_lower_get_child(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t lower, pnanovdb_uint32_t n)
{
	pnanovdb_leaf_handle_t leaf = { lower.address };
	leaf.address = pnanovdb_address_offset64(leaf.address, pnanovdb_int64_as_uint64(pnanovdb_lower_get_table_child(grid_type, buf, lower, n)));
	return leaf;
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_lower_get_value_address_and_level(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t lower, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_uint32_t) level)
{
	pnanovdb_uint32_t n = pnanovdb_lower_coord_to_offset(ijk);
	pnanovdb_address_t value_address;
	if (pnanovdb_lower_get_child_mask(buf, lower, n))
	{
		pnanovdb_leaf_handle_t child = pnanovdb_lower_get_child(grid_type, buf, lower, n);
		value_address = pnanovdb_leaf_get_value_address(grid_type, buf, child, ijk);
		PNANOVDB_DEREF(level) = 0u;
	}
	else
	{
		value_address = pnanovdb_lower_get_table_address(grid_type, buf, lower, n);
		PNANOVDB_DEREF(level) = 1u;
	}
	return value_address;
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_lower_get_value_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t lower, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
	pnanovdb_uint32_t level;
	return pnanovdb_lower_get_value_address_and_level(grid_type, buf, lower, ijk, PNANOVDB_REF(level));
}

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_upper_coord_to_offset(PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
	return (((PNANOVDB_DEREF(ijk).x & 4095) >> 7) << (2 * 5)) +
		(((PNANOVDB_DEREF(ijk).y & 4095) >> 7) << (5)) +
		((PNANOVDB_DEREF(ijk).z & 4095) >> 7);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_upper_get_min_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t node)
{
	pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, upper_off_min);
	return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_upper_get_max_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t node)
{
	pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, upper_off_max);
	return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_upper_get_ave_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t node)
{
	pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, upper_off_ave);
	return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_upper_get_stddev_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t node)
{
	pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, upper_off_stddev);
	return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_upper_get_table_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t node, pnanovdb_uint32_t n)
{
	pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, upper_off_table) + PNANOVDB_GRID_TYPE_GET(grid_type, table_stride) * n;
	return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_upper_get_table_child(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t node, pnanovdb_uint32_t n)
{
	pnanovdb_address_t bufAddress = pnanovdb_upper_get_table_address(grid_type, buf, node, n);
	return pnanovdb_read_int64(buf, bufAddress);
}

PNANOVDB_FORCE_INLINE pnanovdb_lower_handle_t pnanovdb_upper_get_child(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t upper, pnanovdb_uint32_t n)
{
	pnanovdb_lower_handle_t lower = { upper.address };
	lower.address = pnanovdb_address_offset64(lower.address, pnanovdb_int64_as_uint64(pnanovdb_upper_get_table_child(grid_type, buf, upper, n)));
	return lower;
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_upper_get_value_address_and_level(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t upper, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_uint32_t) level)
{
	pnanovdb_uint32_t n = pnanovdb_upper_coord_to_offset(ijk);
	pnanovdb_address_t value_address;
	if (pnanovdb_upper_get_child_mask(buf, upper, n))
	{
		pnanovdb_lower_handle_t child = pnanovdb_upper_get_child(grid_type, buf, upper, n);
		value_address = pnanovdb_lower_get_value_address_and_level(grid_type, buf, child, ijk, level);
	}
	else
	{
		value_address = pnanovdb_upper_get_table_address(grid_type, buf, upper, n);
		PNANOVDB_DEREF(level) = 2u;
	}
	return value_address;
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_upper_get_value_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t upper, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
	pnanovdb_uint32_t level;
	return pnanovdb_upper_get_value_address_and_level(grid_type, buf, upper, ijk, PNANOVDB_REF(level));
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_get_min_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root)
{
	pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, root_off_min);
	return pnanovdb_address_offset(root.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_get_max_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root)
{
	pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, root_off_max);
	return pnanovdb_address_offset(root.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_get_ave_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root)
{
	pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, root_off_ave);
	return pnanovdb_address_offset(root.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_get_stddev_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root)
{
	pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, root_off_stddev);
	return pnanovdb_address_offset(root.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_tile_get_value_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_tile_handle_t root_tile)
{
	pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, root_tile_off_value);
	return pnanovdb_address_offset(root_tile.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_get_value_address_and_level(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_uint32_t) level)
{
	pnanovdb_root_tile_handle_t tile = pnanovdb_root_find_tile(grid_type, buf, root, ijk);
	pnanovdb_address_t ret;
	if (pnanovdb_address_is_null(tile.address))
	{
		ret = pnanovdb_address_offset(root.address, PNANOVDB_GRID_TYPE_GET(grid_type, root_off_background));
		PNANOVDB_DEREF(level) = 4u;
	}
	else if (pnanovdb_int64_is_zero(pnanovdb_root_tile_get_child(buf, tile)))
	{
		ret = pnanovdb_address_offset(tile.address, PNANOVDB_GRID_TYPE_GET(grid_type, root_tile_off_value));
		PNANOVDB_DEREF(level) = 3u;
	}
	else
	{
		pnanovdb_upper_handle_t child = pnanovdb_root_get_child(grid_type, buf, root, tile);
		ret = pnanovdb_upper_get_value_address_and_level(grid_type, buf, child, ijk, level);
	}
	return ret;
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_get_value_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
	pnanovdb_uint32_t level;
	return pnanovdb_root_get_value_address_and_level(grid_type, buf, root, ijk, PNANOVDB_REF(level));
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_get_value_address_bit(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_uint32_t) bit_index)
{
	pnanovdb_uint32_t level;
	pnanovdb_address_t address = pnanovdb_root_get_value_address_and_level(grid_type, buf, root, ijk, PNANOVDB_REF(level));
	PNANOVDB_DEREF(bit_index) = level == 0u ? pnanovdb_int32_as_uint32(PNANOVDB_DEREF(ijk).x & 7) : 0u;
	return address;
}

PNANOVDB_FORCE_INLINE float pnanovdb_root_fp4_read_float(pnanovdb_buf_t buf, pnanovdb_address_t address, PNANOVDB_IN(pnanovdb_coord_t) ijk, pnanovdb_uint32_t level)
{
	float ret;
	if (level == 0)
	{
		ret = pnanovdb_leaf_fp4_read_float(buf, address, ijk);
	}
	else
	{
		ret = pnanovdb_read_float(buf, address);
	}
	return ret;
}

PNANOVDB_FORCE_INLINE float pnanovdb_root_fp8_read_float(pnanovdb_buf_t buf, pnanovdb_address_t address, PNANOVDB_IN(pnanovdb_coord_t) ijk, pnanovdb_uint32_t level)
{
	float ret;
	if (level == 0)
	{
		ret = pnanovdb_leaf_fp8_read_float(buf, address, ijk);
	}
	else
	{
		ret = pnanovdb_read_float(buf, address);
	}
	return ret;
}

PNANOVDB_FORCE_INLINE float pnanovdb_root_fp16_read_float(pnanovdb_buf_t buf, pnanovdb_address_t address, PNANOVDB_IN(pnanovdb_coord_t) ijk, pnanovdb_uint32_t level)
{
	float ret;
	if (level == 0)
	{
		ret = pnanovdb_leaf_fp16_read_float(buf, address, ijk);
	}
	else
	{
		ret = pnanovdb_read_float(buf, address);
	}
	return ret;
}

PNANOVDB_FORCE_INLINE float pnanovdb_root_fpn_read_float(pnanovdb_buf_t buf, pnanovdb_address_t address, PNANOVDB_IN(pnanovdb_coord_t) ijk, pnanovdb_uint32_t level)
{
	float ret;
	if (level == 0)
	{
		ret = pnanovdb_leaf_fpn_read_float(buf, address, ijk);
	}
	else
	{
		ret = pnanovdb_read_float(buf, address);
	}
	return ret;
}

// ------------------------------------------------ ReadAccessor -----------------------------------------------------------

struct pnanovdb_readaccessor_t
{
	pnanovdb_coord_t key;
	pnanovdb_leaf_handle_t leaf;
	pnanovdb_lower_handle_t lower;
	pnanovdb_upper_handle_t upper;
	pnanovdb_root_handle_t root;
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_readaccessor_t)

PNANOVDB_FORCE_INLINE void pnanovdb_readaccessor_init(PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, pnanovdb_root_handle_t root)
{
	PNANOVDB_DEREF(acc).key.x = 0x7FFFFFFF;
	PNANOVDB_DEREF(acc).key.y = 0x7FFFFFFF;
	PNANOVDB_DEREF(acc).key.z = 0x7FFFFFFF;
	PNANOVDB_DEREF(acc).leaf.address = pnanovdb_address_null();
	PNANOVDB_DEREF(acc).lower.address = pnanovdb_address_null();
	PNANOVDB_DEREF(acc).upper.address = pnanovdb_address_null();
	PNANOVDB_DEREF(acc).root = root;
}

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_readaccessor_iscached0(PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, int dirty)
{
	if (pnanovdb_address_is_null(PNANOVDB_DEREF(acc).leaf.address)) { return PNANOVDB_FALSE; }
	if ((dirty & ~((1u << 3) - 1u)) != 0)
	{
		PNANOVDB_DEREF(acc).leaf.address = pnanovdb_address_null();
		return PNANOVDB_FALSE;
	}
	return PNANOVDB_TRUE;
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_readaccessor_iscached1(PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, int dirty)
{
	if (pnanovdb_address_is_null(PNANOVDB_DEREF(acc).lower.address)) { return PNANOVDB_FALSE; }
	if ((dirty & ~((1u << 7) - 1u)) != 0)
	{
		PNANOVDB_DEREF(acc).lower.address = pnanovdb_address_null();
		return PNANOVDB_FALSE;
	}
	return PNANOVDB_TRUE;
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_readaccessor_iscached2(PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, int dirty)
{
	if (pnanovdb_address_is_null(PNANOVDB_DEREF(acc).upper.address)) { return PNANOVDB_FALSE; }
	if ((dirty & ~((1u << 12) - 1u)) != 0)
	{
		PNANOVDB_DEREF(acc).upper.address = pnanovdb_address_null();
		return PNANOVDB_FALSE;
	}
	return PNANOVDB_TRUE;
}
PNANOVDB_FORCE_INLINE int pnanovdb_readaccessor_computedirty(PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
	return (PNANOVDB_DEREF(ijk).x ^ PNANOVDB_DEREF(acc).key.x) | (PNANOVDB_DEREF(ijk).y ^ PNANOVDB_DEREF(acc).key.y) | (PNANOVDB_DEREF(ijk).z ^ PNANOVDB_DEREF(acc).key.z);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_leaf_get_value_address_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_leaf_handle_t leaf, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
	pnanovdb_uint32_t n = pnanovdb_leaf_coord_to_offset(ijk);
	return pnanovdb_leaf_get_table_address(grid_type, buf, leaf, n);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_lower_get_value_address_and_level_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t lower, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, PNANOVDB_INOUT(pnanovdb_uint32_t) level)
{
	pnanovdb_uint32_t n = pnanovdb_lower_coord_to_offset(ijk);
	pnanovdb_address_t value_address;
	if (pnanovdb_lower_get_child_mask(buf, lower, n))
	{
		pnanovdb_leaf_handle_t child = pnanovdb_lower_get_child(grid_type, buf, lower, n);
		PNANOVDB_DEREF(acc).leaf = child;
		PNANOVDB_DEREF(acc).key = PNANOVDB_DEREF(ijk);
		value_address = pnanovdb_leaf_get_value_address_and_cache(grid_type, buf, child, ijk, acc);
		PNANOVDB_DEREF(level) = 0u;
	}
	else
	{
		value_address = pnanovdb_lower_get_table_address(grid_type, buf, lower, n);
		PNANOVDB_DEREF(level) = 1u;
	}
	return value_address;
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_lower_get_value_address_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t lower, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
	pnanovdb_uint32_t level;
	return pnanovdb_lower_get_value_address_and_level_and_cache(grid_type, buf, lower, ijk, acc, PNANOVDB_REF(level));
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_upper_get_value_address_and_level_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t upper, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, PNANOVDB_INOUT(pnanovdb_uint32_t) level)
{
	pnanovdb_uint32_t n = pnanovdb_upper_coord_to_offset(ijk);
	pnanovdb_address_t value_address;
	if (pnanovdb_upper_get_child_mask(buf, upper, n))
	{
		pnanovdb_lower_handle_t child = pnanovdb_upper_get_child(grid_type, buf, upper, n);
		PNANOVDB_DEREF(acc).lower = child;
		PNANOVDB_DEREF(acc).key = PNANOVDB_DEREF(ijk);
		value_address = pnanovdb_lower_get_value_address_and_level_and_cache(grid_type, buf, child, ijk, acc, level);
	}
	else
	{
		value_address = pnanovdb_upper_get_table_address(grid_type, buf, upper, n);
		PNANOVDB_DEREF(level) = 2u;
	}
	return value_address;
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_upper_get_value_address_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t upper, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
	pnanovdb_uint32_t level;
	return pnanovdb_upper_get_value_address_and_level_and_cache(grid_type, buf, upper, ijk, acc, PNANOVDB_REF(level));
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_get_value_address_and_level_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, PNANOVDB_INOUT(pnanovdb_uint32_t) level)
{
	pnanovdb_root_tile_handle_t tile = pnanovdb_root_find_tile(grid_type, buf, root, ijk);
	pnanovdb_address_t ret;
	if (pnanovdb_address_is_null(tile.address))
	{
		ret = pnanovdb_address_offset(root.address, PNANOVDB_GRID_TYPE_GET(grid_type, root_off_background));
		PNANOVDB_DEREF(level) = 4u;
	}
	else if (pnanovdb_int64_is_zero(pnanovdb_root_tile_get_child(buf, tile)))
	{
		ret = pnanovdb_address_offset(tile.address, PNANOVDB_GRID_TYPE_GET(grid_type, root_tile_off_value));
		PNANOVDB_DEREF(level) = 3u;
	}
	else
	{
		pnanovdb_upper_handle_t child = pnanovdb_root_get_child(grid_type, buf, root, tile);
		PNANOVDB_DEREF(acc).upper = child;
		PNANOVDB_DEREF(acc).key = PNANOVDB_DEREF(ijk);
		ret = pnanovdb_upper_get_value_address_and_level_and_cache(grid_type, buf, child, ijk, acc, level);
	}
	return ret;
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_get_value_address_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
	pnanovdb_uint32_t level;
	return pnanovdb_root_get_value_address_and_level_and_cache(grid_type, buf, root, ijk, acc, PNANOVDB_REF(level));
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_readaccessor_get_value_address_and_level(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_uint32_t) level)
{
	int dirty = pnanovdb_readaccessor_computedirty(acc, ijk);

	pnanovdb_address_t value_address;
	if (pnanovdb_readaccessor_iscached0(acc, dirty))
	{
		value_address = pnanovdb_leaf_get_value_address_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).leaf, ijk, acc);
		PNANOVDB_DEREF(level) = 0u;
	}
	else if (pnanovdb_readaccessor_iscached1(acc, dirty))
	{
		value_address = pnanovdb_lower_get_value_address_and_level_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).lower, ijk, acc, level);
	}
	else if (pnanovdb_readaccessor_iscached2(acc, dirty))
	{
		value_address = pnanovdb_upper_get_value_address_and_level_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).upper, ijk, acc, level);
	}
	else
	{
		value_address = pnanovdb_root_get_value_address_and_level_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).root, ijk, acc, level);
	}
	return value_address;
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_readaccessor_get_value_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
	pnanovdb_uint32_t level;
	return pnanovdb_readaccessor_get_value_address_and_level(grid_type, buf, acc, ijk, PNANOVDB_REF(level));
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_readaccessor_get_value_address_bit(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_uint32_t) bit_index)
{
	pnanovdb_uint32_t level;
	pnanovdb_address_t address = pnanovdb_readaccessor_get_value_address_and_level(grid_type, buf, acc, ijk, PNANOVDB_REF(level));
	PNANOVDB_DEREF(bit_index) = level == 0u ? pnanovdb_int32_as_uint32(PNANOVDB_DEREF(ijk).x & 7) : 0u;
	return address;
}

// ------------------------------------------------ ReadAccessor GetDim -----------------------------------------------------------

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_leaf_get_dim_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_leaf_handle_t leaf, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
	return 1u;
}

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_lower_get_dim_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t lower, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
	pnanovdb_uint32_t n = pnanovdb_lower_coord_to_offset(ijk);
	pnanovdb_uint32_t ret;
	if (pnanovdb_lower_get_child_mask(buf, lower, n))
	{
		pnanovdb_leaf_handle_t child = pnanovdb_lower_get_child(grid_type, buf, lower, n);
		PNANOVDB_DEREF(acc).leaf = child;
		PNANOVDB_DEREF(acc).key = PNANOVDB_DEREF(ijk);
		ret = pnanovdb_leaf_get_dim_and_cache(grid_type, buf, child, ijk, acc);
	}
	else
	{
		ret = (1u << (3u)); // node 0 dim
	}
	return ret;
}

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_upper_get_dim_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t upper, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
	pnanovdb_uint32_t n = pnanovdb_upper_coord_to_offset(ijk);
	pnanovdb_uint32_t ret;
	if (pnanovdb_upper_get_child_mask(buf, upper, n))
	{
		pnanovdb_lower_handle_t child = pnanovdb_upper_get_child(grid_type, buf, upper, n);
		PNANOVDB_DEREF(acc).lower = child;
		PNANOVDB_DEREF(acc).key = PNANOVDB_DEREF(ijk);
		ret = pnanovdb_lower_get_dim_and_cache(grid_type, buf, child, ijk, acc);
	}
	else
	{
		ret = (1u << (4u + 3u)); // node 1 dim
	}
	return ret;
}

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_root_get_dim_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
	pnanovdb_root_tile_handle_t tile = pnanovdb_root_find_tile(grid_type, buf, root, ijk);
	pnanovdb_uint32_t ret;
	if (pnanovdb_address_is_null(tile.address))
	{
		ret = 1u << (5u + 4u + 3u); // background, node 2 dim
	}
	else if (pnanovdb_int64_is_zero(pnanovdb_root_tile_get_child(buf, tile)))
	{
		ret = 1u << (5u + 4u + 3u); // tile value, node 2 dim
	}
	else
	{
		pnanovdb_upper_handle_t child = pnanovdb_root_get_child(grid_type, buf, root, tile);
		PNANOVDB_DEREF(acc).upper = child;
		PNANOVDB_DEREF(acc).key = PNANOVDB_DEREF(ijk);
		ret = pnanovdb_upper_get_dim_and_cache(grid_type, buf, child, ijk, acc);
	}
	return ret;
}

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_readaccessor_get_dim(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
	int dirty = pnanovdb_readaccessor_computedirty(acc, ijk);

	pnanovdb_uint32_t dim;
	if (pnanovdb_readaccessor_iscached0(acc, dirty))
	{
		dim = pnanovdb_leaf_get_dim_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).leaf, ijk, acc);
	}
	else if (pnanovdb_readaccessor_iscached1(acc, dirty))
	{
		dim = pnanovdb_lower_get_dim_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).lower, ijk, acc);
	}
	else if (pnanovdb_readaccessor_iscached2(acc, dirty))
	{
		dim = pnanovdb_upper_get_dim_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).upper, ijk, acc);
	}
	else
	{
		dim = pnanovdb_root_get_dim_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).root, ijk, acc);
	}
	return dim;
}

// ------------------------------------------------ ReadAccessor IsActive -----------------------------------------------------------

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_leaf_is_active_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_leaf_handle_t leaf, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
	pnanovdb_uint32_t n = pnanovdb_leaf_coord_to_offset(ijk);
	return pnanovdb_leaf_get_value_mask(buf, leaf, n);
}

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_lower_is_active_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t lower, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
	pnanovdb_uint32_t n = pnanovdb_lower_coord_to_offset(ijk);
	pnanovdb_bool_t is_active;
	if (pnanovdb_lower_get_child_mask(buf, lower, n))
	{
		pnanovdb_leaf_handle_t child = pnanovdb_lower_get_child(grid_type, buf, lower, n);
		PNANOVDB_DEREF(acc).leaf = child;
		PNANOVDB_DEREF(acc).key = PNANOVDB_DEREF(ijk);
		is_active = pnanovdb_leaf_is_active_and_cache(grid_type, buf, child, ijk, acc);
	}
	else
	{
		is_active = pnanovdb_lower_get_value_mask(buf, lower, n);
	}
	return is_active;
}

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_upper_is_active_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t upper, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
	pnanovdb_uint32_t n = pnanovdb_upper_coord_to_offset(ijk);
	pnanovdb_bool_t is_active;
	if (pnanovdb_upper_get_child_mask(buf, upper, n))
	{
		pnanovdb_lower_handle_t child = pnanovdb_upper_get_child(grid_type, buf, upper, n);
		PNANOVDB_DEREF(acc).lower = child;
		PNANOVDB_DEREF(acc).key = PNANOVDB_DEREF(ijk);
		is_active = pnanovdb_lower_is_active_and_cache(grid_type, buf, child, ijk, acc);
	}
	else
	{
		is_active = pnanovdb_upper_get_value_mask(buf, upper, n);
	}
	return is_active;
}

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_root_is_active_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
	pnanovdb_root_tile_handle_t tile = pnanovdb_root_find_tile(grid_type, buf, root, ijk);
	pnanovdb_bool_t is_active;
	if (pnanovdb_address_is_null(tile.address))
	{
		is_active = PNANOVDB_FALSE; // background
	}
	else if (pnanovdb_int64_is_zero(pnanovdb_root_tile_get_child(buf, tile)))
	{
		pnanovdb_uint32_t state = pnanovdb_root_tile_get_state(buf, tile);
		is_active = state != 0u; // tile value
	}
	else
	{
		pnanovdb_upper_handle_t child = pnanovdb_root_get_child(grid_type, buf, root, tile);
		PNANOVDB_DEREF(acc).upper = child;
		PNANOVDB_DEREF(acc).key = PNANOVDB_DEREF(ijk);
		is_active = pnanovdb_upper_is_active_and_cache(grid_type, buf, child, ijk, acc);
	}
	return is_active;
}

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_readaccessor_is_active(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
	int dirty = pnanovdb_readaccessor_computedirty(acc, ijk);

	pnanovdb_bool_t is_active;
	if (pnanovdb_readaccessor_iscached0(acc, dirty))
	{
		is_active = pnanovdb_leaf_is_active_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).leaf, ijk, acc);
	}
	else if (pnanovdb_readaccessor_iscached1(acc, dirty))
	{
		is_active = pnanovdb_lower_is_active_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).lower, ijk, acc);
	}
	else if (pnanovdb_readaccessor_iscached2(acc, dirty))
	{
		is_active = pnanovdb_upper_is_active_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).upper, ijk, acc);
	}
	else
	{
		is_active = pnanovdb_root_is_active_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).root, ijk, acc);
	}
	return is_active;
}

// ------------------------------------------------ Map Transforms -----------------------------------------------------------

PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_map_apply(pnanovdb_buf_t buf, pnanovdb_map_handle_t map, PNANOVDB_IN(pnanovdb_vec3_t) src)
{
	pnanovdb_vec3_t  dst;
	float sx = PNANOVDB_DEREF(src).x;
	float sy = PNANOVDB_DEREF(src).y;
	float sz = PNANOVDB_DEREF(src).z;
	dst.x = sx * pnanovdb_map_get_matf(buf, map, 0) + sy * pnanovdb_map_get_matf(buf, map, 1) + sz * pnanovdb_map_get_matf(buf, map, 2) + pnanovdb_map_get_vecf(buf, map, 0);
	dst.y = sx * pnanovdb_map_get_matf(buf, map, 3) + sy * pnanovdb_map_get_matf(buf, map, 4) + sz * pnanovdb_map_get_matf(buf, map, 5) + pnanovdb_map_get_vecf(buf, map, 1);
	dst.z = sx * pnanovdb_map_get_matf(buf, map, 6) + sy * pnanovdb_map_get_matf(buf, map, 7) + sz * pnanovdb_map_get_matf(buf, map, 8) + pnanovdb_map_get_vecf(buf, map, 2);
	return dst;
}

PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_map_apply_inverse(pnanovdb_buf_t buf, pnanovdb_map_handle_t map, PNANOVDB_IN(pnanovdb_vec3_t) src)
{
	pnanovdb_vec3_t  dst;
	float sx = PNANOVDB_DEREF(src).x - pnanovdb_map_get_vecf(buf, map, 0);
	float sy = PNANOVDB_DEREF(src).y - pnanovdb_map_get_vecf(buf, map, 1);
	float sz = PNANOVDB_DEREF(src).z - pnanovdb_map_get_vecf(buf, map, 2);
	dst.x = sx * pnanovdb_map_get_invmatf(buf, map, 0) + sy * pnanovdb_map_get_invmatf(buf, map, 1) + sz * pnanovdb_map_get_invmatf(buf, map, 2);
	dst.y = sx * pnanovdb_map_get_invmatf(buf, map, 3) + sy * pnanovdb_map_get_invmatf(buf, map, 4) + sz * pnanovdb_map_get_invmatf(buf, map, 5);
	dst.z = sx * pnanovdb_map_get_invmatf(buf, map, 6) + sy * pnanovdb_map_get_invmatf(buf, map, 7) + sz * pnanovdb_map_get_invmatf(buf, map, 8);
	return dst;
}

PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_map_apply_jacobi(pnanovdb_buf_t buf, pnanovdb_map_handle_t map, PNANOVDB_IN(pnanovdb_vec3_t) src)
{
	pnanovdb_vec3_t  dst;
	float sx = PNANOVDB_DEREF(src).x;
	float sy = PNANOVDB_DEREF(src).y;
	float sz = PNANOVDB_DEREF(src).z;
	dst.x = sx * pnanovdb_map_get_matf(buf, map, 0) + sy * pnanovdb_map_get_matf(buf, map, 1) + sz * pnanovdb_map_get_matf(buf, map, 2);
	dst.y = sx * pnanovdb_map_get_matf(buf, map, 3) + sy * pnanovdb_map_get_matf(buf, map, 4) + sz * pnanovdb_map_get_matf(buf, map, 5);
	dst.z = sx * pnanovdb_map_get_matf(buf, map, 6) + sy * pnanovdb_map_get_matf(buf, map, 7) + sz * pnanovdb_map_get_matf(buf, map, 8);
	return dst;
}

PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_map_apply_inverse_jacobi(pnanovdb_buf_t buf, pnanovdb_map_handle_t map, PNANOVDB_IN(pnanovdb_vec3_t) src)
{
	pnanovdb_vec3_t  dst;
	float sx = PNANOVDB_DEREF(src).x;
	float sy = PNANOVDB_DEREF(src).y;
	float sz = PNANOVDB_DEREF(src).z;
	dst.x = sx * pnanovdb_map_get_invmatf(buf, map, 0) + sy * pnanovdb_map_get_invmatf(buf, map, 1) + sz * pnanovdb_map_get_invmatf(buf, map, 2);
	dst.y = sx * pnanovdb_map_get_invmatf(buf, map, 3) + sy * pnanovdb_map_get_invmatf(buf, map, 4) + sz * pnanovdb_map_get_invmatf(buf, map, 5);
	dst.z = sx * pnanovdb_map_get_invmatf(buf, map, 6) + sy * pnanovdb_map_get_invmatf(buf, map, 7) + sz * pnanovdb_map_get_invmatf(buf, map, 8);
	return dst;
}

PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_grid_world_to_indexf(pnanovdb_buf_t buf, pnanovdb_grid_handle_t grid, PNANOVDB_IN(pnanovdb_vec3_t) src)
{
	pnanovdb_map_handle_t map = pnanovdb_grid_get_map(buf, grid);
	return pnanovdb_map_apply_inverse(buf, map, src);
}

PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_grid_index_to_worldf(pnanovdb_buf_t buf, pnanovdb_grid_handle_t grid, PNANOVDB_IN(pnanovdb_vec3_t) src)
{
	pnanovdb_map_handle_t map = pnanovdb_grid_get_map(buf, grid);
	return pnanovdb_map_apply(buf, map, src);
}

PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_grid_world_to_index_dirf(pnanovdb_buf_t buf, pnanovdb_grid_handle_t grid, PNANOVDB_IN(pnanovdb_vec3_t) src)
{
	pnanovdb_map_handle_t map = pnanovdb_grid_get_map(buf, grid);
	return pnanovdb_map_apply_inverse_jacobi(buf, map, src);
}

PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_grid_index_to_world_dirf(pnanovdb_buf_t buf, pnanovdb_grid_handle_t grid, PNANOVDB_IN(pnanovdb_vec3_t) src)
{
	pnanovdb_map_handle_t map = pnanovdb_grid_get_map(buf, grid);
	return pnanovdb_map_apply_jacobi(buf, map, src);
}

// ------------------------------------------------ DitherLUT -----------------------------------------------------------

// This table was generated with
/**************

static constexpr inline uint32
SYSwang_inthash(uint32 key)
{
	// From http://www.concentric.net/~Ttwang/tech/inthash.htm
	key += ~(key << 16);
	key ^=  (key >> 5);
	key +=  (key << 3);
	key ^=  (key >> 13);
	key += ~(key << 9);
	key ^=  (key >> 17);
	return key;
}

static void
ut_initDitherR(float *pattern, float offset,
	int x, int y, int z, int res, int goalres)
{
	// These offsets are designed to maximize the difference between
	// dither values in nearby voxels within a given 2x2x2 cell, without
	// producing axis-aligned artifacts.  The are organized in row-major
	// order.
	static const float	theDitherOffset[] = {0,4,6,2,5,1,3,7};
	static const float	theScale = 0.125F;
	int			key = (((z << res) + y) << res) + x;

	if (res == goalres)
	{
	pattern[key] = offset;
	return;
	}

	// Randomly flip (on each axis) the dithering patterns used by the
	// subcells.  This key is xor'd with the subcell index below before
	// looking up in the dither offset list.
	key = SYSwang_inthash(key) & 7;

	x <<= 1;
	y <<= 1;
	z <<= 1;

	offset *= theScale;
	for (int i = 0; i < 8; i++)
	ut_initDitherR(pattern, offset+theDitherOffset[i ^ key]*theScale,
		x+(i&1), y+((i&2)>>1), z+((i&4)>>2), res+1, goalres);
}

// This is a compact algorithm that accomplishes essentially the same thing
// as ut_initDither() above.  We should eventually switch to use this and
// clean the dead code.
static fpreal32 *
ut_initDitherRecursive(int goalres)
{
	const int nfloat = 1 << (goalres*3);
	float	*pattern = new float[nfloat];
	ut_initDitherR(pattern, 1.0F, 0, 0, 0, 0, goalres);

	// This has built an even spacing from 1/nfloat to 1.0.
	// however, our dither pattern should be 1/(nfloat+1) to nfloat/(nfloat+1)
	// So we do a correction here.  Note that the earlier calculations are
	// done with powers of 2 so are exact, so it does make sense to delay
	// the renormalization to this pass.
	float correctionterm = nfloat / (nfloat+1.0F);
	for (int i = 0; i < nfloat; i++)
		pattern[i] *= correctionterm;
	return pattern;
}

	theDitherMatrix = ut_initDitherRecursive(3);

	for (int i = 0; i < 512/8; i ++)
	{
		for (int j = 0; j < 8; j ++)
			std::cout << theDitherMatrix[i*8+j] << "f, ";
		std::cout << std::endl;
	}

 **************/

PNANOVDB_STATIC_CONST float pnanovdb_dither_lut[512] =
{
	0.14425f, 0.643275f, 0.830409f, 0.331384f, 0.105263f, 0.604289f, 0.167641f, 0.666667f,
	0.892788f, 0.393762f, 0.0818713f, 0.580897f, 0.853801f, 0.354776f, 0.916179f, 0.417154f,
	0.612086f, 0.11306f, 0.79922f, 0.300195f, 0.510721f, 0.0116959f, 0.947368f, 0.448343f,
	0.362573f, 0.861598f, 0.0506823f, 0.549708f, 0.261209f, 0.760234f, 0.19883f, 0.697856f,
	0.140351f, 0.639376f, 0.576998f, 0.0779727f, 0.522417f, 0.0233918f, 0.460039f, 0.959064f,
	0.888889f, 0.389864f, 0.327485f, 0.826511f, 0.272904f, 0.77193f, 0.709552f, 0.210526f,
	0.483431f, 0.982456f, 0.296296f, 0.795322f, 0.116959f, 0.615984f, 0.0545809f, 0.553606f,
	0.732943f, 0.233918f, 0.545809f, 0.0467836f, 0.865497f, 0.366472f, 0.803119f, 0.304094f,
	0.518519f, 0.0194932f, 0.45614f, 0.955166f, 0.729045f, 0.230019f, 0.54191f, 0.042885f,
	0.269006f, 0.768031f, 0.705653f, 0.206628f, 0.479532f, 0.978558f, 0.292398f, 0.791423f,
	0.237817f, 0.736842f, 0.424951f, 0.923977f, 0.136452f, 0.635478f, 0.323587f, 0.822612f,
	0.986355f, 0.487329f, 0.674464f, 0.175439f, 0.88499f, 0.385965f, 0.573099f, 0.0740741f,
	0.51462f, 0.0155945f, 0.202729f, 0.701754f, 0.148148f, 0.647174f, 0.834308f, 0.335283f,
	0.265107f, 0.764133f, 0.951267f, 0.452242f, 0.896686f, 0.397661f, 0.08577f, 0.584795f,
	0.8577f, 0.358674f, 0.920078f, 0.421053f, 0.740741f, 0.241715f, 0.678363f, 0.179337f,
	0.109162f, 0.608187f, 0.17154f, 0.670565f, 0.491228f, 0.990253f, 0.42885f, 0.927875f,
	0.0662768f, 0.565302f, 0.62768f, 0.128655f, 0.183236f, 0.682261f, 0.744639f, 0.245614f,
	0.814815f, 0.315789f, 0.378168f, 0.877193f, 0.931774f, 0.432749f, 0.495127f, 0.994152f,
	0.0350877f, 0.534113f, 0.97076f, 0.471735f, 0.214425f, 0.71345f, 0.526316f, 0.0272904f,
	0.783626f, 0.2846f, 0.222222f, 0.721248f, 0.962963f, 0.463938f, 0.276803f, 0.775828f,
	0.966862f, 0.467836f, 0.405458f, 0.904483f, 0.0701754f, 0.569201f, 0.881092f, 0.382066f,
	0.218324f, 0.717349f, 0.654971f, 0.155945f, 0.818713f, 0.319688f, 0.132554f, 0.631579f,
	0.0623782f, 0.561404f, 0.748538f, 0.249513f, 0.912281f, 0.413255f, 0.974659f, 0.475634f,
	0.810916f, 0.311891f, 0.499025f, 0.998051f, 0.163743f, 0.662768f, 0.226121f, 0.725146f,
	0.690058f, 0.191033f, 0.00389864f, 0.502924f, 0.557505f, 0.0584795f, 0.120858f, 0.619883f,
	0.440546f, 0.939571f, 0.752437f, 0.253411f, 0.307992f, 0.807018f, 0.869396f, 0.37037f,
	0.658869f, 0.159844f, 0.346979f, 0.846004f, 0.588694f, 0.0896686f, 0.152047f, 0.651072f,
	0.409357f, 0.908382f, 0.596491f, 0.0974659f, 0.339181f, 0.838207f, 0.900585f, 0.401559f,
	0.34308f, 0.842105f, 0.779727f, 0.280702f, 0.693957f, 0.194932f, 0.25731f, 0.756335f,
	0.592593f, 0.0935673f, 0.0311891f, 0.530214f, 0.444444f, 0.94347f, 0.506823f, 0.00779727f,
	0.68616f, 0.187135f, 0.124756f, 0.623782f, 0.288499f, 0.787524f, 0.350877f, 0.849903f,
	0.436647f, 0.935673f, 0.873294f, 0.374269f, 0.538012f, 0.0389864f, 0.60039f, 0.101365f,
	0.57115f, 0.0721248f, 0.758285f, 0.259259f, 0.719298f, 0.220273f, 0.532164f, 0.0331384f,
	0.321637f, 0.820663f, 0.00974659f, 0.508772f, 0.469786f, 0.968811f, 0.282651f, 0.781676f,
	0.539961f, 0.0409357f, 0.727096f, 0.22807f, 0.500975f, 0.00194932f, 0.563353f, 0.0643275f,
	0.290448f, 0.789474f, 0.477583f, 0.976608f, 0.251462f, 0.750487f, 0.31384f, 0.812865f,
	0.94152f, 0.442495f, 0.879142f, 0.380117f, 0.37232f, 0.871345f, 0.309942f, 0.808967f,
	0.192982f, 0.692008f, 0.130604f, 0.62963f, 0.621832f, 0.122807f, 0.559454f, 0.0604289f,
	0.660819f, 0.161793f, 0.723197f, 0.224172f, 0.403509f, 0.902534f, 0.840156f, 0.341131f,
	0.411306f, 0.910331f, 0.473684f, 0.97271f, 0.653021f, 0.153996f, 0.0916179f, 0.590643f,
	0.196881f, 0.695906f, 0.384016f, 0.883041f, 0.0955166f, 0.594542f, 0.157895f, 0.65692f,
	0.945419f, 0.446394f, 0.633528f, 0.134503f, 0.844055f, 0.345029f, 0.906433f, 0.407407f,
	0.165692f, 0.664717f, 0.103314f, 0.602339f, 0.126706f, 0.625731f, 0.189084f, 0.688109f,
	0.91423f, 0.415205f, 0.851852f, 0.352827f, 0.875244f, 0.376218f, 0.937622f, 0.438596f,
	0.317739f, 0.816764f, 0.255361f, 0.754386f, 0.996101f, 0.497076f, 0.933723f, 0.434698f,
	0.567251f, 0.0682261f, 0.504873f, 0.00584795f, 0.247563f, 0.746589f, 0.185185f, 0.684211f,
	0.037037f, 0.536062f, 0.0994152f, 0.598441f, 0.777778f, 0.278752f, 0.465887f, 0.964912f,
	0.785575f, 0.28655f, 0.847953f, 0.348928f, 0.0292398f, 0.528265f, 0.7154f, 0.216374f,
	0.39961f, 0.898636f, 0.961014f, 0.461988f, 0.0487329f, 0.547758f, 0.111111f, 0.610136f,
	0.649123f, 0.150097f, 0.212476f, 0.711501f, 0.797271f, 0.298246f, 0.859649f, 0.360624f,
	0.118908f, 0.617934f, 0.0565302f, 0.555556f, 0.329435f, 0.82846f, 0.516569f, 0.0175439f,
	0.867446f, 0.368421f, 0.805068f, 0.306043f, 0.578947f, 0.079922f, 0.267057f, 0.766082f,
	0.270955f, 0.76998f, 0.707602f, 0.208577f, 0.668616f, 0.169591f, 0.606238f, 0.107212f,
	0.520468f, 0.0214425f, 0.45809f, 0.957115f, 0.419103f, 0.918129f, 0.356725f, 0.855751f,
	0.988304f, 0.489279f, 0.426901f, 0.925926f, 0.450292f, 0.949318f, 0.512671f, 0.0136452f,
	0.239766f, 0.738791f, 0.676413f, 0.177388f, 0.699805f, 0.20078f, 0.263158f, 0.762183f,
	0.773879f, 0.274854f, 0.337232f, 0.836257f, 0.672515f, 0.173489f, 0.734893f, 0.235867f,
	0.0253411f, 0.524366f, 0.586745f, 0.0877193f, 0.423002f, 0.922027f, 0.48538f, 0.984405f,
	0.74269f, 0.243665f, 0.680312f, 0.181287f, 0.953216f, 0.454191f, 0.1423f, 0.641326f,
	0.493177f, 0.992203f, 0.430799f, 0.929825f, 0.204678f, 0.703704f, 0.890838f, 0.391813f,
	0.894737f, 0.395712f, 0.0838207f, 0.582846f, 0.0448343f, 0.54386f, 0.231969f, 0.730994f,
	0.146199f, 0.645224f, 0.832359f, 0.333333f, 0.793372f, 0.294347f, 0.980507f, 0.481481f,
	0.364522f, 0.863548f, 0.80117f, 0.302144f, 0.824561f, 0.325536f, 0.138402f, 0.637427f,
	0.614035f, 0.11501f, 0.0526316f, 0.551657f, 0.0760234f, 0.575049f, 0.88694f, 0.387914f,
};

PNANOVDB_FORCE_INLINE float pnanovdb_dither_lookup(pnanovdb_bool_t enabled, int offset)
{
	return enabled ? pnanovdb_dither_lut[offset & 511] : 0.5f;
}

// ------------------------------------------------ HDDA -----------------------------------------------------------

#ifdef PNANOVDB_HDDA

// Comment out to disable this explicit round-off check
#define PNANOVDB_ENFORCE_FORWARD_STEPPING

#define PNANOVDB_HDDA_FLOAT_MAX 1e38f

struct pnanovdb_hdda_t
{
	pnanovdb_int32_t dim;
	float tmin;
	float tmax;
	pnanovdb_coord_t voxel;
	pnanovdb_coord_t step;
	pnanovdb_vec3_t delta;
	pnanovdb_vec3_t next;
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_hdda_t)

PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_hdda_pos_to_ijk(PNANOVDB_IN(pnanovdb_vec3_t) pos)
{
	pnanovdb_coord_t voxel;
	voxel.x = pnanovdb_float_to_int32(pnanovdb_floor(PNANOVDB_DEREF(pos).x));
	voxel.y = pnanovdb_float_to_int32(pnanovdb_floor(PNANOVDB_DEREF(pos).y));
	voxel.z = pnanovdb_float_to_int32(pnanovdb_floor(PNANOVDB_DEREF(pos).z));
	return voxel;
}

PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_hdda_pos_to_voxel(PNANOVDB_IN(pnanovdb_vec3_t) pos, int dim)
{
	pnanovdb_coord_t voxel;
	voxel.x = pnanovdb_float_to_int32(pnanovdb_floor(PNANOVDB_DEREF(pos).x)) & (~(dim - 1));
	voxel.y = pnanovdb_float_to_int32(pnanovdb_floor(PNANOVDB_DEREF(pos).y)) & (~(dim - 1));
	voxel.z = pnanovdb_float_to_int32(pnanovdb_floor(PNANOVDB_DEREF(pos).z)) & (~(dim - 1));
	return voxel;
}

PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_hdda_ray_start(PNANOVDB_IN(pnanovdb_vec3_t) origin, float tmin, PNANOVDB_IN(pnanovdb_vec3_t) direction)
{
	pnanovdb_vec3_t pos = pnanovdb_vec3_add(
		pnanovdb_vec3_mul(PNANOVDB_DEREF(direction), pnanovdb_vec3_uniform(tmin)),
		PNANOVDB_DEREF(origin)
	);
	return pos;
}

PNANOVDB_FORCE_INLINE void pnanovdb_hdda_init(PNANOVDB_INOUT(pnanovdb_hdda_t) hdda, PNANOVDB_IN(pnanovdb_vec3_t) origin, float tmin, PNANOVDB_IN(pnanovdb_vec3_t) direction, float tmax, int dim)
{
	PNANOVDB_DEREF(hdda).dim = dim;
	PNANOVDB_DEREF(hdda).tmin = tmin;
	PNANOVDB_DEREF(hdda).tmax = tmax;

	pnanovdb_vec3_t pos = pnanovdb_hdda_ray_start(origin, tmin, direction);
	pnanovdb_vec3_t dir_inv = pnanovdb_vec3_div(pnanovdb_vec3_uniform(1.f), PNANOVDB_DEREF(direction));

	PNANOVDB_DEREF(hdda).voxel = pnanovdb_hdda_pos_to_voxel(PNANOVDB_REF(pos), dim);

	// x
	if (PNANOVDB_DEREF(direction).x == 0.f)
	{
		PNANOVDB_DEREF(hdda).next.x = PNANOVDB_HDDA_FLOAT_MAX;
		PNANOVDB_DEREF(hdda).step.x = 0;
		PNANOVDB_DEREF(hdda).delta.x = 0.f;
	}
	else if (dir_inv.x > 0.f)
	{
		PNANOVDB_DEREF(hdda).step.x = 1;
		PNANOVDB_DEREF(hdda).next.x = PNANOVDB_DEREF(hdda).tmin + (PNANOVDB_DEREF(hdda).voxel.x + dim - pos.x) * dir_inv.x;
		PNANOVDB_DEREF(hdda).delta.x = dir_inv.x;
	}
	else
	{
		PNANOVDB_DEREF(hdda).step.x = -1;
		PNANOVDB_DEREF(hdda).next.x = PNANOVDB_DEREF(hdda).tmin + (PNANOVDB_DEREF(hdda).voxel.x - pos.x) * dir_inv.x;
		PNANOVDB_DEREF(hdda).delta.x = -dir_inv.x;
	}

	// y
	if (PNANOVDB_DEREF(direction).y == 0.f)
	{
		PNANOVDB_DEREF(hdda).next.y = PNANOVDB_HDDA_FLOAT_MAX;
		PNANOVDB_DEREF(hdda).step.y = 0;
		PNANOVDB_DEREF(hdda).delta.y = 0.f;
	}
	else if (dir_inv.y > 0.f)
	{
		PNANOVDB_DEREF(hdda).step.y = 1;
		PNANOVDB_DEREF(hdda).next.y = PNANOVDB_DEREF(hdda).tmin + (PNANOVDB_DEREF(hdda).voxel.y + dim - pos.y) * dir_inv.y;
		PNANOVDB_DEREF(hdda).delta.y = dir_inv.y;
	}
	else
	{
		PNANOVDB_DEREF(hdda).step.y = -1;
		PNANOVDB_DEREF(hdda).next.y = PNANOVDB_DEREF(hdda).tmin + (PNANOVDB_DEREF(hdda).voxel.y - pos.y) * dir_inv.y;
		PNANOVDB_DEREF(hdda).delta.y = -dir_inv.y;
	}

	// z
	if (PNANOVDB_DEREF(direction).z == 0.f)
	{
		PNANOVDB_DEREF(hdda).next.z = PNANOVDB_HDDA_FLOAT_MAX;
		PNANOVDB_DEREF(hdda).step.z = 0;
		PNANOVDB_DEREF(hdda).delta.z = 0.f;
	}
	else if (dir_inv.z > 0.f)
	{
		PNANOVDB_DEREF(hdda).step.z = 1;
		PNANOVDB_DEREF(hdda).next.z = PNANOVDB_DEREF(hdda).tmin + (PNANOVDB_DEREF(hdda).voxel.z + dim - pos.z) * dir_inv.z;
		PNANOVDB_DEREF(hdda).delta.z = dir_inv.z;
	}
	else
	{
		PNANOVDB_DEREF(hdda).step.z = -1;
		PNANOVDB_DEREF(hdda).next.z = PNANOVDB_DEREF(hdda).tmin + (PNANOVDB_DEREF(hdda).voxel.z - pos.z) * dir_inv.z;
		PNANOVDB_DEREF(hdda).delta.z = -dir_inv.z;
	}
}

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_hdda_update(PNANOVDB_INOUT(pnanovdb_hdda_t) hdda, PNANOVDB_IN(pnanovdb_vec3_t) origin, PNANOVDB_IN(pnanovdb_vec3_t) direction, int dim)
{
	if (PNANOVDB_DEREF(hdda).dim == dim)
	{
		return PNANOVDB_FALSE;
	}
	PNANOVDB_DEREF(hdda).dim = dim;

	pnanovdb_vec3_t pos = pnanovdb_vec3_add(
		pnanovdb_vec3_mul(PNANOVDB_DEREF(direction), pnanovdb_vec3_uniform(PNANOVDB_DEREF(hdda).tmin)),
		PNANOVDB_DEREF(origin)
	);
	pnanovdb_vec3_t dir_inv = pnanovdb_vec3_div(pnanovdb_vec3_uniform(1.f), PNANOVDB_DEREF(direction));

	PNANOVDB_DEREF(hdda).voxel = pnanovdb_hdda_pos_to_voxel(PNANOVDB_REF(pos), dim);

	if (PNANOVDB_DEREF(hdda).step.x != 0)
	{
		PNANOVDB_DEREF(hdda).next.x = PNANOVDB_DEREF(hdda).tmin + (PNANOVDB_DEREF(hdda).voxel.x - pos.x) * dir_inv.x;
		if (PNANOVDB_DEREF(hdda).step.x > 0)
		{
			PNANOVDB_DEREF(hdda).next.x += dim * dir_inv.x;
		}
	}
	if (PNANOVDB_DEREF(hdda).step.y != 0)
	{
		PNANOVDB_DEREF(hdda).next.y = PNANOVDB_DEREF(hdda).tmin + (PNANOVDB_DEREF(hdda).voxel.y - pos.y) * dir_inv.y;
		if (PNANOVDB_DEREF(hdda).step.y > 0)
		{
			PNANOVDB_DEREF(hdda).next.y += dim * dir_inv.y;
		}
	}
	if (PNANOVDB_DEREF(hdda).step.z != 0)
	{
		PNANOVDB_DEREF(hdda).next.z = PNANOVDB_DEREF(hdda).tmin + (PNANOVDB_DEREF(hdda).voxel.z - pos.z) * dir_inv.z;
		if (PNANOVDB_DEREF(hdda).step.z > 0)
		{
			PNANOVDB_DEREF(hdda).next.z += dim * dir_inv.z;
		}
	}

	return PNANOVDB_TRUE;
}

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_hdda_step(PNANOVDB_INOUT(pnanovdb_hdda_t) hdda)
{
	pnanovdb_bool_t ret;
	if (PNANOVDB_DEREF(hdda).next.x < PNANOVDB_DEREF(hdda).next.y && PNANOVDB_DEREF(hdda).next.x < PNANOVDB_DEREF(hdda).next.z)
	{
#ifdef PNANOVDB_ENFORCE_FORWARD_STEPPING
		if (PNANOVDB_DEREF(hdda).next.x <= PNANOVDB_DEREF(hdda).tmin)
		{
			PNANOVDB_DEREF(hdda).next.x += PNANOVDB_DEREF(hdda).tmin - 0.999999f * PNANOVDB_DEREF(hdda).next.x + 1.0e-6f;
		}
#endif
		PNANOVDB_DEREF(hdda).tmin = PNANOVDB_DEREF(hdda).next.x;
		PNANOVDB_DEREF(hdda).next.x += PNANOVDB_DEREF(hdda).dim * PNANOVDB_DEREF(hdda).delta.x;
		PNANOVDB_DEREF(hdda).voxel.x += PNANOVDB_DEREF(hdda).dim * PNANOVDB_DEREF(hdda).step.x;
		ret = PNANOVDB_DEREF(hdda).tmin <= PNANOVDB_DEREF(hdda).tmax;
	}
	else if (PNANOVDB_DEREF(hdda).next.y < PNANOVDB_DEREF(hdda).next.z)
	{
#ifdef PNANOVDB_ENFORCE_FORWARD_STEPPING
		if (PNANOVDB_DEREF(hdda).next.y <= PNANOVDB_DEREF(hdda).tmin)
		{
			PNANOVDB_DEREF(hdda).next.y += PNANOVDB_DEREF(hdda).tmin - 0.999999f * PNANOVDB_DEREF(hdda).next.y + 1.0e-6f;
		}
#endif
		PNANOVDB_DEREF(hdda).tmin = PNANOVDB_DEREF(hdda).next.y;
		PNANOVDB_DEREF(hdda).next.y += PNANOVDB_DEREF(hdda).dim * PNANOVDB_DEREF(hdda).delta.y;
		PNANOVDB_DEREF(hdda).voxel.y += PNANOVDB_DEREF(hdda).dim * PNANOVDB_DEREF(hdda).step.y;
		ret = PNANOVDB_DEREF(hdda).tmin <= PNANOVDB_DEREF(hdda).tmax;
	}
	else
	{
#ifdef PNANOVDB_ENFORCE_FORWARD_STEPPING
		if (PNANOVDB_DEREF(hdda).next.z <= PNANOVDB_DEREF(hdda).tmin)
		{
			PNANOVDB_DEREF(hdda).next.z += PNANOVDB_DEREF(hdda).tmin - 0.999999f * PNANOVDB_DEREF(hdda).next.z + 1.0e-6f;
		}
#endif
		PNANOVDB_DEREF(hdda).tmin = PNANOVDB_DEREF(hdda).next.z;
		PNANOVDB_DEREF(hdda).next.z += PNANOVDB_DEREF(hdda).dim * PNANOVDB_DEREF(hdda).delta.z;
		PNANOVDB_DEREF(hdda).voxel.z += PNANOVDB_DEREF(hdda).dim * PNANOVDB_DEREF(hdda).step.z;
		ret = PNANOVDB_DEREF(hdda).tmin <= PNANOVDB_DEREF(hdda).tmax;
	}
	return ret;
}

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_hdda_ray_clip(
	PNANOVDB_IN(pnanovdb_vec3_t) bbox_min, 
	PNANOVDB_IN(pnanovdb_vec3_t) bbox_max, 
	PNANOVDB_IN(pnanovdb_vec3_t) origin, PNANOVDB_INOUT(float) tmin, 
	PNANOVDB_IN(pnanovdb_vec3_t) direction, PNANOVDB_INOUT(float) tmax
)
{
	pnanovdb_vec3_t dir_inv = pnanovdb_vec3_div(pnanovdb_vec3_uniform(1.f), PNANOVDB_DEREF(direction));
	pnanovdb_vec3_t t0 = pnanovdb_vec3_mul(pnanovdb_vec3_sub(PNANOVDB_DEREF(bbox_min), PNANOVDB_DEREF(origin)), dir_inv);
	pnanovdb_vec3_t t1 = pnanovdb_vec3_mul(pnanovdb_vec3_sub(PNANOVDB_DEREF(bbox_max), PNANOVDB_DEREF(origin)), dir_inv);
	pnanovdb_vec3_t tmin3 = pnanovdb_vec3_min(t0, t1);
	pnanovdb_vec3_t tmax3 = pnanovdb_vec3_max(t0, t1);
	float tnear = pnanovdb_max(tmin3.x, pnanovdb_max(tmin3.y, tmin3.z));
	float tfar = pnanovdb_min(tmax3.x, pnanovdb_min(tmax3.y, tmax3.z));
	pnanovdb_bool_t hit = tnear <= tfar;
	PNANOVDB_DEREF(tmin) = pnanovdb_max(PNANOVDB_DEREF(tmin), tnear);
	PNANOVDB_DEREF(tmax) = pnanovdb_min(PNANOVDB_DEREF(tmax), tfar);
	return hit;
}

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_hdda_zero_crossing(
	pnanovdb_grid_type_t grid_type,
	pnanovdb_buf_t buf, 
	PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, 
	PNANOVDB_IN(pnanovdb_vec3_t) origin, float tmin,
	PNANOVDB_IN(pnanovdb_vec3_t) direction, float tmax,
	PNANOVDB_INOUT(float) thit,
	PNANOVDB_INOUT(float) v
)
{
	pnanovdb_coord_t bbox_min = pnanovdb_root_get_bbox_min(buf, PNANOVDB_DEREF(acc).root);
	pnanovdb_coord_t bbox_max = pnanovdb_root_get_bbox_max(buf, PNANOVDB_DEREF(acc).root);
	pnanovdb_vec3_t bbox_minf = pnanovdb_coord_to_vec3(bbox_min);
	pnanovdb_vec3_t bbox_maxf = pnanovdb_coord_to_vec3(pnanovdb_coord_add(bbox_max, pnanovdb_coord_uniform(1)));

	pnanovdb_bool_t hit = pnanovdb_hdda_ray_clip(PNANOVDB_REF(bbox_minf), PNANOVDB_REF(bbox_maxf), origin, PNANOVDB_REF(tmin), direction, PNANOVDB_REF(tmax));
	if (!hit || tmax > 1.0e20f)
	{
		return PNANOVDB_FALSE;
	}

	pnanovdb_vec3_t pos = pnanovdb_hdda_ray_start(origin, tmin, direction);
	pnanovdb_coord_t ijk = pnanovdb_hdda_pos_to_ijk(PNANOVDB_REF(pos));

	pnanovdb_address_t address = pnanovdb_readaccessor_get_value_address(PNANOVDB_GRID_TYPE_FLOAT, buf, acc, PNANOVDB_REF(ijk));
	float v0 = pnanovdb_read_float(buf, address);

	pnanovdb_int32_t dim = pnanovdb_uint32_as_int32(pnanovdb_readaccessor_get_dim(PNANOVDB_GRID_TYPE_FLOAT, buf, acc, PNANOVDB_REF(ijk)));
	pnanovdb_hdda_t hdda;
	pnanovdb_hdda_init(PNANOVDB_REF(hdda), origin, tmin, direction, tmax, dim);
	while (pnanovdb_hdda_step(PNANOVDB_REF(hdda)))
	{
		pnanovdb_vec3_t pos_start = pnanovdb_hdda_ray_start(origin, hdda.tmin + 1.0001f, direction);
		ijk = pnanovdb_hdda_pos_to_ijk(PNANOVDB_REF(pos_start));
		dim = pnanovdb_uint32_as_int32(pnanovdb_readaccessor_get_dim(PNANOVDB_GRID_TYPE_FLOAT, buf, acc, PNANOVDB_REF(ijk)));
		pnanovdb_hdda_update(PNANOVDB_REF(hdda), origin, direction, dim);
		if (hdda.dim > 1 || !pnanovdb_readaccessor_is_active(grid_type, buf, acc, PNANOVDB_REF(ijk)))
		{
			continue;
		}
		while (pnanovdb_hdda_step(PNANOVDB_REF(hdda)) && pnanovdb_readaccessor_is_active(grid_type, buf, acc, PNANOVDB_REF(hdda.voxel)))
		{
			ijk = hdda.voxel;
			pnanovdb_address_t address = pnanovdb_readaccessor_get_value_address(PNANOVDB_GRID_TYPE_FLOAT, buf, acc, PNANOVDB_REF(ijk));
			PNANOVDB_DEREF(v) = pnanovdb_read_float(buf, address);
			if (PNANOVDB_DEREF(v) * v0 < 0.f)
			{
				PNANOVDB_DEREF(thit) = hdda.tmin;
				return PNANOVDB_TRUE;
			}
		}
	}
	return PNANOVDB_FALSE;
}

#endif

#endif // end of NANOVDB_PNANOVDB_H_HAS_BEEN_INCLUDED
